"Fossies" - the Fresh Open Source Software Archive

Member "zorp-7.0.4/modules/http/http.cc" (28 Oct 2019, 135132 Bytes) of package /linux/privat/zorp-7.0.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "http.cc" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 7.0.1_vs_7.0.2.

    1 /***************************************************************************
    2  *
    3  * Copyright (c) 2000-2015 BalaBit IT Ltd, Budapest, Hungary
    4  * Copyright (c) 2015-2018 BalaSys IT Ltd, Budapest, Hungary
    5  *
    6  * This program is free software; you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License as published by
    8  * the Free Software Foundation; either version 2 of the License, or
    9  * (at your option) any later version.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU General Public License along
   17  * with this program; if not, write to the Free Software Foundation, Inc.,
   18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   19  *
   20  *   Slightly based on the code by: Viktor Peter Kovacs <vps__@freemail.hu>
   21  *
   22  ***************************************************************************/
   23 
   24 #include "http.h"
   25 
   26 #include <zorpll/thread.h>
   27 #include <zorpll/registry.h>
   28 #include <zorpll/log.h>
   29 #include <zorp/policy.h>
   30 #include <zorp/authprovider.h>
   31 #include <zorpll/misc.h>
   32 #include <zorp/policy.h>
   33 #include <zorpll/code_base64.h>
   34 #include <zorpll/streamblob.h>
   35 #include <zorpll/streamline.h>
   36 #include <zorpll/random.h>
   37 #include <zorpll/code.h>
   38 #include <zorpll/code_base64.h>
   39 
   40 #include <zorp/proxy/errorloader.h>
   41 
   42 #include <ctype.h>
   43 #include <sys/socket.h>
   44 #include <unistd.h>
   45 #include <fcntl.h>
   46 
   47 #include <memory>
   48 #include <functional>
   49 #include <algorithm>
   50 
   51 #include <netdb.h>
   52 static GHashTable *auth_hash = NULL;
   53 G_LOCK_DEFINE_STATIC(auth_mutex);
   54 
   55 const std::string HttpProxy::zorp_realm_cookie_name("ZorpRealm");
   56 
   57 typedef struct _ZorpAuthInfo
   58 {
   59   time_t last_auth_time;
   60   time_t accept_credit_until;
   61   time_t created_at;
   62   gchar *username;
   63   gchar **groups;
   64   gchar *basic_auth_creds;
   65 } ZorpAuthInfo;
   66 
   67 /**
   68  * http_filter_hash_compare:
   69  * @a: first item
   70  * @b: second item
   71  *
   72  * This function is the hash compare function for request header hashes.
   73  **/
   74 gint
   75 http_filter_hash_compare(gconstpointer a, gconstpointer b)
   76 {
   77   z_enter();
   78 
   79   if (strcasecmp((char *) a, (char *) b) == 0)
   80     z_return(1);
   81 
   82   z_return(0);
   83 }
   84 
   85 /**
   86  * http_filter_hash_bucket:
   87  * @a: item to calculate hash value for
   88  *
   89  * This function is the hash calculation function for request header hashes.
   90  **/
   91 gint
   92 http_filter_hash_bucket(gconstpointer a)
   93 {
   94   int sum = 0;
   95   char *s = (char *) a;
   96 
   97   z_enter();
   98 
   99   while (*s != 0)
  100     {
  101       sum += toupper(*s);
  102       s++;
  103     }
  104 
  105   z_return(sum % 16);
  106 }
  107 
  108 /**
  109  * http_config_set_defaults:
  110  * @self: HttpProxy instance
  111  *
  112  * This function initializes various attributes exported to the Python layer
  113  * for possible modification.
  114  **/
  115 static void
  116 http_config_set_defaults(HttpProxy *self)
  117 {
  118   z_proxy_enter(self);
  119   self->connection_mode = HTTP_CONNECTION_CLOSE;
  120   self->server_connection_mode = HTTP_CONNECTION_CLOSE;
  121   self->force_reconnect = FALSE;
  122   self->transparent_mode = TRUE;
  123   self->permit_server_requests = TRUE;
  124   self->permit_proxy_requests = FALSE;
  125   self->permit_unicode_url = FALSE;
  126   self->permit_http09_responses = TRUE;
  127 
  128   self->rewrite_host_header = TRUE;
  129   self->require_host_header = TRUE;
  130   self->strict_header_checking = FALSE;
  131   self->strict_header_checking_action = ZV_DROP;
  132   self->permit_null_response = TRUE;
  133   self->max_line_length = 4096;
  134   self->max_url_length = 4096;
  135   self->max_header_lines = 50;
  136   self->max_hostname_length = 256;
  137   self->max_chunk_length = 0;
  138   self->timeout_request = 10000;
  139   self->timeout_response = 300000;
  140   self->timeout = 300000;
  141   self->default_http_port = 80;
  142   self->default_https_port = 443;
  143   self->default_ftp_port = 21;
  144   self->use_default_port_in_transparent_mode = TRUE;
  145   self->use_canonicalized_urls = TRUE;
  146   self->max_body_length = 0;
  147   self->buffer_size = 1500;
  148   self->rerequest_attempts = 0;
  149 
  150   http_init_headers(&self->headers[EP_CLIENT]);
  151   http_init_headers(&self->headers[EP_SERVER]);
  152 
  153   http_init_url(&self->request_url_parts);
  154   self->request_url = g_string_sized_new(128);
  155 
  156   self->current_header_name = g_string_sized_new(16);
  157   self->current_header_value = g_string_sized_new(32);
  158 
  159   self->parent_proxy = g_string_sized_new(0);
  160   self->parent_proxy_port = 3128;
  161 
  162   self->target_port_range = g_string_new("80,443");
  163   self->auth_header_value = g_string_sized_new(32);
  164 
  165   self->remote_server = g_string_sized_new(32);
  166   self->connected_server = g_string_sized_new(32);
  167   self->request_method = g_string_sized_new(16);
  168   self->response_msg = g_string_sized_new(32);
  169 
  170   self->request_method_policy =
  171     g_hash_table_new((GHashFunc) http_filter_hash_bucket,
  172                      (GCompareFunc) http_filter_hash_compare);
  173   self->request_header_policy =
  174     g_hash_table_new((GHashFunc) http_filter_hash_bucket,
  175                      (GCompareFunc) http_filter_hash_compare);
  176 
  177   self->response_policy =
  178     z_dim_hash_table_new(1, 2, DIMHASH_WILDCARD, DIMHASH_CONSUME);
  179   self->response_header_policy =
  180     g_hash_table_new((GHashFunc) http_filter_hash_bucket,
  181                      (GCompareFunc) http_filter_hash_compare);
  182 
  183   self->error_info = g_string_sized_new(0);
  184   self->error_msg = g_string_sized_new(0);
  185   self->error_headers = g_string_sized_new(0);
  186   self->error_code = HTTP_MSG_NOT_ASSIGNED;
  187   self->error_status = 500;
  188   self->error_files_directory = g_string_sized_new(0);
  189 
  190   self->error_silent = FALSE;
  191 
  192   self->send_custom_response = FALSE;
  193   self->custom_response_body = g_string_sized_new(0);
  194 
  195   self->max_auth_time = 0;
  196 
  197   self->auth_realm = g_string_new("Zorp HTTP auth");
  198   self->old_auth_header = g_string_sized_new(0);
  199   self->auth_by_cookie = FALSE;
  200   self->auth_by_form = FALSE;
  201   self->login_page_path = g_string_sized_new(0);
  202 
  203 
  204   self->request_categories = NULL;
  205 
  206   z_proxy_return(self);
  207 }
  208 
  209 /**
  210  * http_config_init:
  211  * @self: HttpProxy instance
  212  *
  213  * This function is called right after the config() method to initialize
  214  * settings the Python layer specified for us.
  215  **/
  216 static void
  217 http_config_init(HttpProxy *self)
  218 {
  219   z_proxy_enter(self);
  220 
  221   if (self->max_line_length > HTTP_MAX_LINE)
  222     self->max_line_length = HTTP_MAX_LINE;
  223 
  224   self->super.endpoints[EP_CLIENT]->timeout = self->timeout_request;
  225   self->poll = z_poll_new();
  226   z_proxy_return(self);
  227 }
  228 
  229 /**
  230  * http_query_request_version:
  231  * @self: HttpProxy instance
  232  * @name: name of requested variable
  233  * @value: unused
  234  *
  235  * This function is registered as a Z_VAR_TYPE_CUSTOM get handler, e.g. it
  236  * is called whenever one of the request_version attribute is requested from
  237  * Python. Instead of presetting that attribute before calling into Python
  238  * we calculate its value dynamically.
  239  **/
  240 static ZPolicyObj *
  241 http_query_request_version(HttpProxy *self, gchar *name, gpointer  /* value */)
  242 {
  243   ZPolicyObj *res = NULL;
  244 
  245   z_proxy_enter(self);
  246 
  247   if (strcmp(name, "request_version") == 0)
  248     res = z_policy_var_build("s#", self->request_version, strlen(self->request_version));
  249 
  250   z_proxy_return(self, res);
  251 }
  252 
  253 /**
  254  * http_query_request_url:
  255  * @self: HttpProxy instance
  256  * @name: name of requested variable
  257  * @value: unused
  258  *
  259  * This function is registered as a Z_VAR_TYPE_CUSTOM get handler, e.g. it
  260  * is called whenever one of the request_url_* attributes are requested from
  261  * Python. Instead of presetting those attributes before calling into Python
  262  * we calculate their value dynamically.
  263  **/
  264 static ZPolicyObj *
  265 http_query_request_url(HttpProxy *self, gchar *name, gpointer  /* value */)
  266 {
  267   ZPolicyObj *res = NULL;
  268 
  269   z_proxy_enter(self);
  270 
  271   if (strcmp(name, "request_url") == 0)
  272     res = z_policy_var_build("s#", self->request_url->str, self->request_url->len);
  273   else if (strcmp(name, "request_url_proto") == 0 || strcmp(name, "request_url_scheme") == 0)
  274     res = z_policy_var_build("s#", self->request_url_parts.scheme->str, self->request_url_parts.scheme->len);
  275   else if (strcmp(name, "request_url_username") == 0)
  276     res = z_policy_var_build("s#", self->request_url_parts.user->str, self->request_url_parts.user->len);
  277   else if (strcmp(name, "request_url_passwd") == 0)
  278     res = z_policy_var_build("s#", self->request_url_parts.passwd->str, self->request_url_parts.passwd->len);
  279   else if (strcmp(name, "request_url_host") == 0)
  280     res = z_policy_var_build("s#", self->request_url_parts.host->str, self->request_url_parts.host->len);
  281   else if (strcmp(name, "request_url_port") == 0)
  282     res = z_policy_var_build("i", self->request_url_parts.port ? self->request_url_parts.port : self->default_http_port);
  283   else if (strcmp(name, "request_url_file") == 0)
  284     res = z_policy_var_build("s#", self->request_url_parts.file->str, self->request_url_parts.file->len);
  285   else if (strcmp(name, "request_url_query") == 0)
  286     res = z_policy_var_build("s#", self->request_url_parts.query->str, self->request_url_parts.query->len);
  287   else
  288     PyErr_SetString(PyExc_AttributeError, "Unknown attribute");
  289 
  290   z_proxy_return(self, res);
  291 }
  292 
  293 /**
  294  * http_set_request_url:
  295  * @self: HttpProxy instance
  296  * @name: name of requested variable
  297  * @value: unused
  298  * @new: new value for attribute
  299  *
  300  * This function is registered as a Z_VAR_TYPE_CUSTOM set handler, e.g. it
  301  * is called whenever the request_url attribute are changed from Python.
  302  * We need to reparse the URL in these cases.
  303  **/
  304 static gint
  305 http_set_request_url(HttpProxy *self, gchar *name, gpointer  /* value */, PyObject *new_)
  306 {
  307   z_proxy_enter(self);
  308 
  309   if (strcmp(name, "request_url") == 0)
  310     {
  311       gchar *str;
  312       const gchar *reason;
  313 
  314       if (!PyArg_Parse(new_, "s", &str))
  315         z_proxy_return(self, -1);
  316 
  317       if (!http_parse_url(&self->request_url_parts, self->permit_unicode_url,
  318                           self->permit_invalid_hex_escape, FALSE, str, &reason))
  319         {
  320           z_proxy_log(self, HTTP_ERROR, 2, "Policy tried to force an invalid URL; url='%s', reason='%s'", str, reason);
  321           z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid URL.");
  322           z_proxy_return(self, -1);
  323         }
  324 
  325       if (!http_format_url(&self->request_url_parts, self->request_url, TRUE, self->permit_unicode_url, TRUE, &reason))
  326         {
  327           z_proxy_log(self, HTTP_ERROR, 2, "Error canonicalizing URL; url='%s', reason='%s'", str, reason);
  328           z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid URL.");
  329           z_proxy_return(self, -1);
  330         }
  331 
  332       z_proxy_return(self, 0);
  333     }
  334 
  335   z_policy_raise_exception_obj(z_policy_exc_attribute_error, "Can only set request_url");
  336   z_proxy_return(self, -1);
  337 }
  338 
  339 /**
  340  * http_query_mime_type:
  341  * @self: HttpProxy instance
  342  * @name: name of requested variable
  343  * @value: unused
  344  *
  345  * This function is registered as a Z_VAR_TYPE_CUSTOM get handler, e.g. it
  346  * is called whenever one of the request_mime_type or response_mime_type
  347  * attributes are requested from Python. Instead of presetting those
  348  * attributes before calling into Python we calculate their value
  349  * dynamically.
  350  **/
  351 static ZPolicyObj *
  352 http_query_mime_type(HttpProxy *self, gchar *name, gpointer  /* value */)
  353 {
  354   ZPolicyObj *res = NULL;
  355   HttpHeader *hdr;
  356   gboolean success;
  357 
  358   z_proxy_enter(self);
  359 
  360   if (strcmp(name, "request_mime_type") == 0)
  361     {
  362       success = http_lookup_header(&self->headers[EP_CLIENT], "Content-Type", &hdr);
  363     }
  364   else if (strcmp(name, "response_mime_type") == 0)
  365     {
  366       success = http_lookup_header(&self->headers[EP_SERVER], "Content-Type", &hdr);
  367     }
  368   else
  369     {
  370       PyErr_SetString(PyExc_AttributeError, "Unknown attribute");
  371       z_proxy_return(self, NULL);
  372     }
  373 
  374   if (!success || !hdr)
  375     {
  376       res = PyString_FromString("");
  377     }
  378   else
  379     {
  380       gchar *start, *end;
  381 
  382       start = hdr->value->str;
  383 
  384       while (*start == ' ')
  385         start++;
  386 
  387       end = strchr(hdr->value->str, ';');
  388 
  389       if (end)
  390         {
  391           end--;
  392 
  393           while (end > start && *end == ' ')
  394             end--;
  395         }
  396 
  397       if (end)
  398         res = PyString_FromStringAndSize(hdr->value->str, (end - start + 1));
  399       else
  400         res = PyString_FromString(hdr->value->str);
  401     }
  402 
  403   z_proxy_return(self, res);
  404 }
  405 
  406 static ZPolicyObj *
  407 http_policy_header_manip(HttpProxy *self, ZPolicyObj *args)
  408 {
  409   gint action, side;
  410   gchar *header, *new_value = NULL;
  411   HttpHeader *p = NULL;
  412   ZPolicyObj *res = NULL;
  413 
  414   z_proxy_enter(self);
  415 
  416   if (!z_policy_var_parse_tuple(args, "iis|s", &action, &side, &header, &new_value))
  417     goto error;
  418 
  419   side &= 1;
  420 
  421   switch (action)
  422     {
  423     case 0:
  424 
  425       /* get */
  426       if (http_lookup_header(&self->headers[side], header, &p))
  427         {
  428           res = z_policy_var_build("s", p->value->str);
  429         }
  430       else
  431         {
  432           res = z_policy_none_ref();
  433         }
  434 
  435       break;
  436 
  437     case 1:
  438 
  439       /* set */
  440       if (!new_value)
  441         goto error_set_exc;
  442 
  443       if (!http_lookup_header(&self->headers[side], header, &p))
  444         p = http_add_header(&self->headers[side], header, strlen(header), new_value, strlen(new_value));
  445 
  446       g_string_assign(p->value, new_value);
  447       p->present = TRUE;
  448       res = z_policy_none_ref();
  449       break;
  450 
  451     default:
  452       goto error_set_exc;
  453     }
  454 
  455   z_proxy_return(self, res);
  456 
  457  error_set_exc:
  458   z_policy_raise_exception_obj(z_policy_exc_value_error, "Invalid arguments.");
  459 
  460  error:
  461   z_proxy_return(self, NULL);
  462 
  463 }
  464 
  465 static ZPolicyObj *
  466 http_query_headers_flat(HttpProxy *self, gint side)
  467 {
  468   ZPolicyObj *res = NULL;
  469 
  470   z_proxy_enter(self);
  471 
  472   side &= 1;
  473   res = z_policy_var_build("s#", self->headers[side].flat->str, strlen(self->headers[side].flat->str));
  474 
  475   z_proxy_return(self, res);
  476 }
  477 
  478 static ZPolicyObj *
  479 http_query_request_headers_flat(HttpProxy *self, gchar * /* name */, gpointer  /* value */)
  480 {
  481   return http_query_headers_flat(self, EP_CLIENT);
  482 }
  483 
  484 static ZPolicyObj *
  485 http_query_response_headers_flat(HttpProxy *self, gchar * /* name */, gpointer  /* value */)
  486 {
  487   return http_query_headers_flat(self, EP_SERVER);
  488 }
  489 
  490 
  491 /**
  492  * http_register_vars:
  493  * @self: HttpProxy instance
  494  *
  495  * This function is called upon startup to export Python attributes.
  496  **/
  497 static void
  498 http_register_vars(HttpProxy *self)
  499 {
  500   z_proxy_enter(self);
  501   z_proxy_var_new(&self->super, "transparent_mode",
  502                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  503                   &self->transparent_mode);
  504 
  505   z_proxy_var_new(&self->super, "permit_server_requests",
  506                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  507                   &self->permit_server_requests);
  508 
  509   z_proxy_var_new(&self->super, "permit_null_response",
  510                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  511                   &self->permit_null_response);
  512 
  513   z_proxy_var_new(&self->super, "permit_proxy_requests",
  514                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  515                   &self->permit_proxy_requests);
  516 
  517   z_proxy_var_new(&self->super, "permit_unicode_url",
  518                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  519                   &self->permit_unicode_url);
  520 
  521   z_proxy_var_new(&self->super, "permit_invalid_hex_escape",
  522                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  523                   &self->permit_invalid_hex_escape);
  524 
  525   z_proxy_var_new(&self->super, "permit_http09_responses",
  526                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  527                   &self->permit_http09_responses);
  528 
  529   z_proxy_var_new(&self->super, "permit_ftp_over_http",
  530                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  531                   &self->permit_ftp_over_http);
  532 
  533   /* close or keep-alive */
  534   z_proxy_var_new(&self->super, "connection_mode",
  535                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET,
  536                   &self->connection_mode);
  537 
  538   z_proxy_var_new(&self->super, "keep_persistent",
  539                   Z_VAR_TYPE_INT | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG | Z_VAR_GET,
  540                   &self->keep_persistent);
  541 
  542   /* string containing parent proxy */
  543   z_proxy_var_new(&self->super, "parent_proxy",
  544                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  545                   self->parent_proxy);
  546 
  547   /* parent proxy port */
  548   z_proxy_var_new(&self->super, "parent_proxy_port",
  549                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  550                   &self->parent_proxy_port);
  551 
  552   /* default port if portnumber is not specified in urls */
  553   z_proxy_var_new(&self->super, "default_port",
  554                   Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  555                   "default_http_port");
  556 
  557   z_proxy_var_new(&self->super, "use_default_port_in_transparent_mode",
  558                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  559                   &self->use_default_port_in_transparent_mode);
  560 
  561   z_proxy_var_new(&self->super, "use_canonicalized_urls",
  562                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  563                   &self->use_canonicalized_urls);
  564 
  565   z_proxy_var_new(&self->super, "default_http_port",
  566                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  567                   &self->default_http_port);
  568 
  569   z_proxy_var_new(&self->super, "default_https_port",
  570                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  571                   &self->default_https_port);
  572 
  573   z_proxy_var_new(&self->super, "default_ftp_port",
  574                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  575                   &self->default_ftp_port);
  576 
  577   /* rewrite host header when redirecting */
  578   z_proxy_var_new(&self->super, "rewrite_host_header",
  579                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  580                   &self->rewrite_host_header);
  581 
  582   z_proxy_var_new(&self->super, "reset_on_close",
  583                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  584                   &self->reset_on_close);
  585 
  586   /* require host header */
  587   z_proxy_var_new(&self->super, "require_host_header",
  588                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  589                   &self->require_host_header);
  590 
  591   /* enable strict header checking */
  592   z_proxy_var_new(&self->super, "strict_header_checking",
  593                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  594                   &self->strict_header_checking);
  595 
  596   /* enable strict header checking */
  597   z_proxy_var_new(&self->super, "strict_header_checking_action",
  598                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  599                   &self->strict_header_checking_action);
  600 
  601   /* integer */
  602   z_proxy_var_new(&self->super, "max_line_length",
  603                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  604                   &self->max_line_length);
  605 
  606   /* integer */
  607   z_proxy_var_new(&self->super, "max_url_length",
  608                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  609                   &self->max_url_length);
  610 
  611   /* integer */
  612   z_proxy_var_new(&self->super, "max_hostname_length",
  613                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  614                   &self->max_hostname_length);
  615 
  616   /* integer */
  617   z_proxy_var_new(&self->super, "max_header_lines",
  618                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  619                   &self->max_header_lines);
  620 
  621   /* integer */
  622   z_proxy_var_new(&self->super, "max_keepalive_requests",
  623                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  624                   &self->max_keepalive_requests);
  625 
  626   /* integer */
  627   z_proxy_var_new(&self->super, "max_body_length",
  628                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  629                   &self->max_body_length);
  630 
  631   /* integer */
  632   z_proxy_var_new(&self->super, "max_chunk_length",
  633                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  634                   &self->max_chunk_length);
  635 
  636   /* integer */
  637   z_proxy_var_new(&self->super, "request_count",
  638                   Z_VAR_TYPE_INT | Z_VAR_GET,
  639                   &self->request_count);
  640 
  641   /* timeout value in milliseconds */
  642   z_proxy_var_new(&self->super, "timeout",
  643                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  644                   &self->timeout);
  645 
  646   /* timeout value in milliseconds */
  647   z_proxy_var_new(&self->super, "buffer_size",
  648                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  649                   &self->buffer_size);
  650 
  651   /* request timeout value in milliseconds */
  652   z_proxy_var_new(&self->super, "timeout_request",
  653                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG,
  654                   &self->timeout_request);
  655 
  656   /* response timeout value in milliseconds */
  657   z_proxy_var_new(&self->super, "timeout_response",
  658                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG,
  659                   &self->timeout_response);
  660 
  661   /* number of rerequest attempts */
  662   z_proxy_var_new(&self->super, "rerequest_attempts",
  663                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG,
  664                   &self->rerequest_attempts);
  665 
  666   /* hash indexed by request method */
  667   z_proxy_var_new(&self->super, "request",
  668                   Z_VAR_TYPE_HASH | Z_VAR_GET | Z_VAR_GET_CONFIG,
  669                   self->request_method_policy);
  670 
  671   /* hash indexed by header name */
  672   z_proxy_var_new(&self->super, "request_header",
  673                   Z_VAR_TYPE_HASH | Z_VAR_GET | Z_VAR_GET_CONFIG,
  674                   self->request_header_policy);
  675 
  676   /* string containing current request headers as a raw string */
  677   z_proxy_var_new(&self->super, "request_headers_flat",
  678                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  679                   NULL, http_query_request_headers_flat, NULL, NULL);
  680 
  681   /* hash indexed by response code */
  682   z_proxy_var_new(&self->super, "response",
  683                   Z_VAR_TYPE_DIMHASH | Z_VAR_GET | Z_VAR_GET_CONFIG,
  684                   self->response_policy);
  685 
  686   /* hash indexed by header name */
  687   z_proxy_var_new(&self->super, "response_header",
  688                   Z_VAR_TYPE_HASH | Z_VAR_GET | Z_VAR_GET_CONFIG,
  689                   self->response_header_policy);
  690 
  691   /* string containing current response headers as a raw string */
  692   z_proxy_var_new(&self->super, "response_headers_flat",
  693                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  694                   NULL, http_query_response_headers_flat, NULL, NULL);
  695 
  696   /* header manipulation */
  697   z_proxy_var_new(&self->super, "_AbstractHttpProxy__headerManip",
  698                   Z_VAR_TYPE_METHOD | Z_VAR_GET,
  699                   self, http_policy_header_manip);
  700 
  701   /* string containing current url proto */
  702   z_proxy_var_new(&self->super, "request_method",
  703                   Z_VAR_TYPE_STRING | Z_VAR_GET,
  704                   self->request_method);
  705 
  706   /* string containing current url version */
  707   z_proxy_var_new(&self->super, "request_version",
  708                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  709                   NULL, http_query_request_version, NULL, NULL);
  710 
  711   /* string containing current url */
  712   z_proxy_var_new(&self->super, "request_url",
  713                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET | Z_VAR_SET,
  714                   NULL, http_query_request_url, http_set_request_url, NULL);
  715 
  716   /* string containing current url proto */
  717   z_proxy_var_new(&self->super, "request_url_proto",
  718                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  719                   NULL, http_query_request_url, NULL, NULL);
  720 
  721   /* string containing current url proto */
  722   z_proxy_var_new(&self->super, "request_url_scheme",
  723                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  724                   NULL, http_query_request_url, NULL, NULL);
  725 
  726   /* string containing current url username */
  727   z_proxy_var_new(&self->super, "request_url_username",
  728                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  729                   NULL, http_query_request_url, NULL, NULL);
  730 
  731   /* string containing current url passwd */
  732   z_proxy_var_new(&self->super, "request_url_passwd",
  733                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  734                   NULL, http_query_request_url, NULL, NULL);
  735 
  736   /* string containing current url hostname */
  737   z_proxy_var_new(&self->super, "request_url_host",
  738                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  739                   NULL, http_query_request_url, NULL, NULL);
  740 
  741   /* string containing current url port */
  742   z_proxy_var_new(&self->super, "request_url_port",
  743                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  744                   NULL, http_query_request_url, NULL, NULL);
  745 
  746   /* string containing current url file */
  747   z_proxy_var_new(&self->super, "request_url_file",
  748                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  749                   NULL, http_query_request_url, NULL, NULL);
  750 
  751   /* string containing current url query */
  752   z_proxy_var_new(&self->super, "request_url_query",
  753                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  754                   NULL, http_query_request_url, NULL, NULL);
  755 
  756   /* string containing current url fragment */
  757   z_proxy_var_new(&self->super, "request_url_fragment",
  758                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  759                   NULL, http_query_request_url, NULL, NULL);
  760 
  761   /* string containing request mime type */
  762   z_proxy_var_new(&self->super, "request_mime_type",
  763                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  764                   NULL, http_query_mime_type, NULL, NULL);
  765 
  766   /* string containing response mime type */
  767   z_proxy_var_new(&self->super, "response_mime_type",
  768                   Z_VAR_TYPE_CUSTOM | Z_VAR_GET,
  769                   NULL, http_query_mime_type, NULL, NULL);
  770 
  771   /* string containing current header name */
  772   z_proxy_var_new(&self->super, "current_header_name",
  773                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET,
  774                   self->current_header_name);
  775 
  776   /* string containing current header value */
  777   z_proxy_var_new(&self->super, "current_header_value",
  778                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET,
  779                   self->current_header_value);
  780 
  781   /* error response */
  782   z_proxy_var_new(&self->super, "error_status",
  783                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  784                   &self->error_status);
  785 
  786   /* error silence */
  787   z_proxy_var_new(&self->super, "error_silent",
  788                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  789                   &self->error_silent);
  790 
  791   /* string inserted into error messages */
  792   z_proxy_var_new(&self->super, "error_info",
  793                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET,
  794                   self->error_info);
  795 
  796   z_proxy_var_new(&self->super, "error_msg",
  797                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET,
  798                   self->error_msg);
  799 
  800   z_proxy_var_new(&self->super, "error_headers",
  801                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET,
  802                   self->error_headers);
  803 
  804   z_proxy_var_new(&self->super, "custom_response_body",
  805                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET,
  806                   self->custom_response_body);
  807 
  808   z_proxy_var_new(&self->super, "auth_forward",
  809                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG,
  810                   &self->auth_forward);
  811 
  812   z_proxy_var_new(&self->super, "auth",
  813                   Z_VAR_TYPE_OBJECT | Z_VAR_GET | Z_VAR_SET_CONFIG,
  814                   &self->auth);
  815 
  816   z_proxy_var_new(&self->super, "auth_realm",
  817                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET_CONFIG,
  818                   self->auth_realm);
  819 
  820   z_proxy_var_new(&self->super, "max_auth_time",
  821                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG,
  822                   &self->max_auth_time);
  823 
  824   z_proxy_var_new(&self->super, "target_port_range",
  825                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG,
  826                   self->target_port_range);
  827 
  828   z_proxy_var_new(&self->super, "error_files_directory",
  829                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG,
  830                   self->error_files_directory);
  831 
  832   /* compatibility with Zorp 0.8.x */
  833   z_proxy_var_new(&self->super, "transparent_server_requests",
  834                   Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  835                   "permit_server_requests");
  836 
  837   z_proxy_var_new(&self->super, "transparent_proxy_requests",
  838                   Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  839                   "permit_proxy_requests");
  840 
  841   z_proxy_var_new(&self->super, "request_timeout",
  842                   Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET | Z_VAR_GET_CONFIG | Z_VAR_SET_CONFIG,
  843                   "timeout_request");
  844 
  845   z_proxy_var_new(&self->super, "request_headers",
  846                   Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_GET_CONFIG,
  847                   "request_header");
  848 
  849   z_proxy_var_new(&self->super, "response_headers",
  850                   Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_GET_CONFIG,
  851                   "response_header");
  852 
  853   z_proxy_var_new(&self->super, "url_proto",
  854                   Z_VAR_TYPE_ALIAS | Z_VAR_GET,
  855                   "request_url_proto");
  856 
  857   z_proxy_var_new(&self->super, "url_username",
  858                   Z_VAR_TYPE_ALIAS | Z_VAR_GET,
  859                   "request_url_username");
  860 
  861   z_proxy_var_new(&self->super, "url_passwd",
  862                   Z_VAR_TYPE_ALIAS | Z_VAR_GET,
  863                   "request_url_passwd");
  864 
  865   z_proxy_var_new(&self->super, "url_host",
  866                   Z_VAR_TYPE_ALIAS | Z_VAR_GET,
  867                   "request_url_host");
  868 
  869   z_proxy_var_new(&self->super, "url_port",
  870                   Z_VAR_TYPE_ALIAS | Z_VAR_GET,
  871                   "request_url_port");
  872 
  873   z_proxy_var_new(&self->super, "url_file",
  874                   Z_VAR_TYPE_ALIAS | Z_VAR_GET,
  875                   "request_url_file");
  876 
  877   z_proxy_var_new(&self->super, "error_response",
  878                   Z_VAR_TYPE_ALIAS | Z_VAR_GET | Z_VAR_SET,
  879                   "error_status");
  880 
  881   z_proxy_var_new(&self->super, "request_host",
  882                   Z_VAR_TYPE_ALIAS | Z_VAR_GET,
  883                   "request_url_host");
  884 
  885   z_proxy_var_new(&self->super, "auth_by_cookie",
  886                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  887                   &self->auth_by_cookie);
  888 
  889   z_proxy_var_new(&self->super, "auth_by_form",
  890                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  891                   &self->auth_by_form);
  892 
  893   z_proxy_var_new(&self->super, "login_page_path",
  894                   Z_VAR_TYPE_STRING | Z_VAR_GET | Z_VAR_SET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  895                   self->login_page_path);
  896 
  897   z_proxy_var_new(&self->super, "auth_cache_time",
  898                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  899                   &self->auth_cache_time);
  900 
  901   z_proxy_var_new(&self->super, "auth_cache_update",
  902                   Z_VAR_TYPE_INT | Z_VAR_GET | Z_VAR_SET_CONFIG | Z_VAR_GET_CONFIG,
  903                   &self->auth_cache_update);
  904 
  905 
  906   z_proxy_var_new(&self->super, "request_categories",
  907                   Z_VAR_TYPE_OBJECT | Z_VAR_GET,
  908                   &self->request_categories);
  909 
  910   z_proxy_return(self);
  911 }
  912 
  913 static std::string
  914 http_error_file_path(HttpProxy *self, const std::string &filename)
  915 {
  916   std::string file_path;
  917   if (self->error_files_directory->len)
  918     file_path = std::string(self->error_files_directory->str) + "/" + filename;
  919   else
  920     file_path = ZORP_DATADIR "/http/" + std::string(self->super.language->str) + "/" + filename;
  921 
  922   return file_path;
  923 }
  924 
  925 /**
  926  * http_error_message:
  927  * @self: HttpProxy instance
  928  * @response_code: HTTP status code to return
  929  * @message_code: HTTP message code (one of HTTP_MSG_*)
  930  * @infomsg: additional information added into error messages
  931  *
  932  * This function formats and returns an error page to the client when its
  933  * request cannot be fulfilled. It switches to non-persistent mode and
  934  * prepares the proxy for shutdown.
  935  **/
  936 static gboolean
  937 http_error_message(HttpProxy *self, gint response_code, guint message_code, GString *infomsg)
  938 {
  939   const gchar *messages[] =
  940     {
  941       NULL,
  942       "clientsyntax.html",
  943       "serversyntax.html",
  944       "policysyntax.html",
  945       "policyviolation.html",
  946       "invalidurl.html",
  947       "connecterror.html",
  948       "ioerror.html",
  949       "auth.html",
  950       "clienttimeout.html",
  951       "servertimeout.html",
  952       "badcontent.html",
  953       "ftperror.html",
  954       "redirect.html"
  955     };
  956   gchar response[256];
  957   gchar *error_msg;
  958 
  959   z_proxy_enter(self);
  960 
  961   if (message_code != HTTP_MSG_CONNECT_ERROR)
  962     {
  963       z_policy_lock(self->super.thread);
  964       z_proxy_report_policy_abort(&(self->super));
  965       z_policy_unlock(self->super.thread);
  966     }
  967 
  968   if (message_code >= (sizeof(messages) / sizeof(char *)))
  969     {
  970       /*LOG
  971         This message indicates that Zorp caught an invalid error code
  972         internally. Please report this event to the BalaSys Development Team
  973     (at devel@balasys.hu).
  974       */
  975       z_proxy_log(self, HTTP_ERROR, 2, "Internal error, error code out of range; error_code='%d'", message_code);
  976       z_proxy_return(self, FALSE);
  977     }
  978 
  979   if (message_code == 0)
  980     z_proxy_return(self, TRUE);
  981 
  982   if (self->proto_version[EP_CLIENT] >= 0x0100)
  983     {
  984       g_snprintf(response, sizeof(response), "HTTP/1.0 %d %s\r\n", response_code, self->error_msg->len > 0 ? self->error_msg->str : "Error encountered");
  985 
  986       if (http_write(self, EP_CLIENT, response, strlen(response)) != G_IO_STATUS_NORMAL)
  987         z_proxy_return(self, FALSE); /* error writing error message is already sent by http_write */
  988 
  989       /* FIXME: we should not use self->headers[EP_SERVER] for this purpose */
  990       g_string_truncate(self->headers[EP_SERVER].flat, 0);
  991 
  992       if (!self->transparent_mode)
  993         g_string_append(self->headers[EP_SERVER].flat, "Proxy-Connection: close\r\n");
  994       else
  995         g_string_append(self->headers[EP_SERVER].flat, "Connection: close\r\n");
  996 
  997       g_string_append(self->headers[EP_SERVER].flat, self->error_headers->str);
  998       g_string_append(self->headers[EP_SERVER].flat, "Content-Type: text/html\r\n\r\n");
  999 
 1000       if (http_write(self, EP_CLIENT, self->headers[EP_SERVER].flat->str, self->headers[EP_SERVER].flat->len) != G_IO_STATUS_NORMAL)
 1001         z_proxy_return(self, FALSE);
 1002     }
 1003 
 1004   if ((self->request_flags & HTTP_REQ_FLG_HEAD))
 1005     z_proxy_return(self, TRUE); /* we are responding to a HEAD request, do not return a body */
 1006 
 1007   std::string filename = http_error_file_path(self, messages[message_code]);
 1008 
 1009   if (self->error_silent)
 1010     {
 1011       /*LOG
 1012         This message reports that Zorp would send the given error page to
 1013         the browser, if silent mode would not be enabled. It is likely that
 1014         some protocol/configuration/proxy error occurred.
 1015       */
 1016       z_proxy_log(self, HTTP_DEBUG, 6, "An error occurred, would serve error file, but silent mode is enabled; filename='%s'", filename.c_str());
 1017       z_proxy_return(self, FALSE);
 1018     }
 1019 
 1020   /*LOG
 1021     This message reports that Zorp is sending the given error page to the
 1022     clients browser. It is likely that some protocol/configuration/proxy
 1023     error occurred.
 1024   */
 1025   z_proxy_log(self, HTTP_DEBUG, 6, "An error occurred, serving error file; filename='%s'", filename.c_str());
 1026   error_msg = z_error_loader_format_file(filename.c_str(), infomsg->str, Z_EF_ESCAPE_HTML, NULL, NULL);
 1027 
 1028   if (error_msg)
 1029     {
 1030       http_write(self, EP_CLIENT, error_msg, strlen(error_msg));
 1031       g_free(error_msg);
 1032     }
 1033 
 1034   z_proxy_return(self, TRUE);
 1035 }
 1036 
 1037 /**
 1038  * Send a locally generated response back to the client
 1039  *
 1040  * The response will be sent back as is, with the supplied status info and headers.
 1041  * The empty line between the headers and response body will be added automatically.
 1042  * Note that a Content-Length header should be included in the headers, depending on the
 1043  * request method and the status code, as described in rfc2616.
 1044  * A Connection: close header will be added automatically.
 1045  *
 1046  * @param self The proxy.
 1047  * @param status_code Status code to use in the status line.
 1048  * @param status_msg Status message to use in the status line.
 1049  * @param headers Flat string containing all the headers to send to the client.
 1050  * @param response_body Response body to send to the client. May be empty.
 1051  */
 1052 static void
 1053 http_send_custom_response(HttpProxy *self, guint status_code, GString *status_msg, GString *headers, GString *response_body)
 1054 {
 1055   gchar status_line[256];
 1056   gsize response_body_length = response_body ? response_body->len : 0;
 1057   /* Preallocate some space for the response:
 1058    * status line + error headers + appended headers + empty line + response body
 1059    */
 1060   GString *http_response = g_string_sized_new(sizeof(status_line)
 1061                                               + headers->len +
 1062                                               + 32 + 2
 1063                                               + response_body_length);
 1064 
 1065 
 1066   g_snprintf(status_line, sizeof(status_line), "HTTP/1.0 %d %s\r\n",
 1067              status_code, status_msg->len > 0 ? status_msg->str : "Error encountered");
 1068 
 1069   g_string_append(http_response, status_line);
 1070   g_string_append(http_response, headers->str);
 1071 
 1072   g_string_append(http_response, "Connection: close\r\n");
 1073   g_string_append(http_response, "\r\n");
 1074 
 1075   if (response_body_length > 0)
 1076     g_string_append(http_response, response_body->str);
 1077 
 1078   z_log(NULL, HTTP_DEBUG, 6, "Sending custom response back to the client; length='%" G_GSIZE_FORMAT "'", http_response->len);
 1079 
 1080   if (http_write(self, EP_CLIENT, http_response->str, http_response->len) != G_IO_STATUS_NORMAL)
 1081     z_log(NULL, HTTP_ERROR, 4, "Error sending custom response back to the client;");
 1082 
 1083   g_string_free(http_response, TRUE);
 1084 }
 1085 
 1086 /**
 1087  * http_client_stream_init:
 1088  * @self: HttpProxy instance
 1089  *
 1090  * This function is called upon startup to initialize our client stream.
 1091  **/
 1092 static gboolean
 1093 http_client_stream_init(HttpProxy *self)
 1094 {
 1095   ZStream *tmpstream;
 1096   z_proxy_enter(self);
 1097 
 1098   z_proxy_enter(self);
 1099   tmpstream = self->super.endpoints[EP_CLIENT];
 1100   self->super.endpoints[EP_CLIENT] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF | ZRL_PARTIAL_READ);
 1101   z_stream_unref(tmpstream);
 1102   /* timeout is initialized after the config event */
 1103   z_proxy_return(self, TRUE);
 1104 }
 1105 
 1106 /**
 1107  * http_server_stream_init:
 1108  * @self: HttpProxy instance
 1109  *
 1110  * This function is called upon startup to initialize our server stream.
 1111  **/
 1112 static gboolean
 1113 http_server_stream_init(HttpProxy *self)
 1114 {
 1115   ZStream *tmpstream;
 1116   z_proxy_enter(self);
 1117 
 1118   z_proxy_enter(self);
 1119   tmpstream = self->super.endpoints[EP_SERVER];
 1120   self->super.endpoints[EP_SERVER] = z_stream_line_new(tmpstream, self->max_line_length, ZRL_EOL_CRLF | ZRL_PARTIAL_READ);
 1121   z_stream_unref(tmpstream);
 1122   self->super.endpoints[EP_SERVER]->timeout = self->timeout;
 1123   z_proxy_return(self, TRUE);
 1124 }
 1125 
 1126 /**
 1127  * http_server_stream_is_initialized:
 1128  * @self: HttpProxy instance
 1129  *
 1130  * This function returns whether or not the server stream has already
 1131  * been initialized. Currently, it just checks if a StreamLine has been
 1132  * pushed onto the stack.
 1133  */
 1134 static gboolean
 1135 http_server_stream_is_initialized(HttpProxy *self)
 1136 {
 1137   gboolean res;
 1138 
 1139   z_proxy_enter(self);
 1140 
 1141   res = (z_stream_search_stack(self->super.endpoints[EP_SERVER], G_IO_IN, Z_CLASS(ZStreamLine)) != NULL);
 1142 
 1143   z_proxy_return(self, res);
 1144 }
 1145 
 1146 GIOStatus
 1147 http_write(HttpProxy *self, guint side, gchar *buf, size_t buflen)
 1148 {
 1149   GIOStatus res;
 1150   gsize bytes_written;
 1151 
 1152   z_proxy_enter(self);
 1153 
 1154   if (!self->super.endpoints[side])
 1155     {
 1156       /*LOG
 1157         This message reports that Zorp was about to write to an invalid
 1158         stream. Please report this event to the BalaSys Development Team (at
 1159         devel@balasys.hu).
 1160       */
 1161       z_proxy_log(self, HTTP_ERROR, 1, "Error writing stream, stream is NULL; side='%s'", side == EP_CLIENT ? "client" : "server");
 1162       z_proxy_return(self, G_IO_STATUS_ERROR);
 1163     }
 1164 
 1165   res = z_stream_write(self->super.endpoints[side], buf, buflen, &bytes_written, NULL);
 1166 
 1167   if (res != G_IO_STATUS_NORMAL || buflen != bytes_written)
 1168     {
 1169       /* FIXME: move this to a separate function */
 1170       /*LOG
 1171         This message reports that Zorp was unable to write to the given
 1172         stream. It is likely that the peer closed the connection
 1173         unexpectedly.
 1174       */
 1175       z_proxy_log(self, HTTP_ERROR, 1, "Error writing stream; side='%s', res='%x', error='%s'", side == EP_CLIENT ? "client" : "server", res, g_strerror(errno));
 1176 
 1177       self->error_code = HTTP_MSG_IO_ERROR;
 1178       self->error_status = 502;
 1179       g_string_sprintf(self->error_info, "Error writing to %s (%s)", side == EP_CLIENT ? "client" : "server", g_strerror(errno));
 1180       z_proxy_return(self, G_IO_STATUS_ERROR);
 1181     }
 1182 
 1183   z_proxy_return(self, res);
 1184 }
 1185 
 1186 static int
 1187 http_parse_connection_hdr_value(HttpProxy *self, HttpHeader *hdr)
 1188 {
 1189   z_proxy_enter(self);
 1190 
 1191   if (strcasecmp(hdr->value->str, "keep-alive") == 0)
 1192     z_proxy_return(self, HTTP_CONNECTION_KEEPALIVE);
 1193   else if (strcasecmp(hdr->value->str, "close") == 0)
 1194     z_proxy_return(self, HTTP_CONNECTION_CLOSE);
 1195 
 1196   z_proxy_log(self, HTTP_ERROR, 4, "Unknown connection header value; value='%s'", hdr->value->str);
 1197   z_proxy_return(self, HTTP_CONNECTION_UNKNOWN);
 1198 }
 1199 
 1200 static void
 1201 http_assign_connection_hdr_value(HttpProxy *self, GString *value)
 1202 {
 1203   z_proxy_enter(self);
 1204 
 1205   if (self->connection_mode == HTTP_CONNECTION_KEEPALIVE)
 1206     g_string_assign(value, "keep-alive");
 1207   else if (self->connection_mode == HTTP_CONNECTION_CLOSE)
 1208     g_string_assign(value, "close");
 1209 
 1210   z_proxy_return(self);
 1211 }
 1212 
 1213 static inline gboolean
 1214 http_parent_proxy_enabled(HttpProxy *self)
 1215 {
 1216   return !!self->parent_proxy->len;
 1217 }
 1218 
 1219 static gboolean
 1220 http_decode_base64(gchar *dst, guint dstlen, const gchar *src, guint srclen)
 1221 {
 1222   z_enter();
 1223   ZCode * base64_decode = z_code_base64_decode_new(0, FALSE);
 1224 
 1225   if (!z_code_transform(base64_decode, src, srclen) ||
 1226       !z_code_finish(base64_decode))
 1227     {
 1228       z_code_free(base64_decode);
 1229       z_return(FALSE);
 1230     }
 1231 
 1232   dstlen = z_code_get_result(base64_decode, dst, dstlen - 1);
 1233   dst[dstlen] = 0;
 1234   z_code_free(base64_decode);
 1235   z_return(TRUE);
 1236 }
 1237 
 1238 static std::string
 1239 http_encode_base64(const std::string &src)
 1240 {
 1241   z_enter();
 1242   ZCode *base64_encode = z_code_base64_encode_new(0, 0);
 1243 
 1244   if (!z_code_transform(base64_encode, src.c_str(), src.length()) ||
 1245       !z_code_finish(base64_encode))
 1246     {
 1247       z_code_free(base64_encode);
 1248       z_return("");
 1249     }
 1250 
 1251   gsize retlen = z_code_get_result_length(base64_encode);
 1252   char *dst = g_new(gchar, retlen + 1);
 1253   gsize dstlen = z_code_get_result(base64_encode, dst, retlen);
 1254   std::string result(dst, dstlen);
 1255   g_free(dst);
 1256   z_code_free(base64_encode);
 1257   z_return(result);
 1258 }
 1259 
 1260 static bool
 1261 http_do_authenticate(HttpProxy *self, ZorpAuthInfo *auth_info, const std::string &username, const std::string &password)
 1262 {
 1263   z_policy_lock(self->super.thread);
 1264   bool res = z_auth_provider_check_passwd(self->auth, self->super.session_id, const_cast<gchar*>(username.c_str()), const_cast<gchar *>(password.c_str()), &auth_info->groups, &self->super);
 1265   z_policy_unlock(self->super.thread);
 1266 
 1267   if (res)
 1268     {
 1269       res = z_proxy_user_authenticated_default(&self->super, username.c_str(), (const gchar **)auth_info->groups);
 1270       G_LOCK(auth_mutex);
 1271 
 1272       if (self->auth_cache_time > 0)
 1273         {
 1274           auth_info->last_auth_time = time(NULL);
 1275         }
 1276 
 1277       g_free(auth_info->username);
 1278       auth_info->username = g_strdup(username.c_str());
 1279 
 1280       if (self->auth_forward)
 1281         {
 1282           z_proxy_log(self, HTTP_DEBUG, 5, "Assembling synthetic Authorization header for auth_forward;");
 1283 
 1284           std::string encoded_creds = http_encode_base64(username + ':' + password);
 1285           if (encoded_creds.empty())
 1286             {
 1287               z_proxy_log(self, HTTP_ERROR, 3,
 1288                           "Error encoding synthetic Authorization header for auth_forward;");
 1289               res = FALSE;
 1290             }
 1291           else
 1292             {
 1293               encoded_creds.insert(0, "Basic ");
 1294               auth_info->basic_auth_creds = g_strdup(encoded_creds.c_str());
 1295             }
 1296         }
 1297 
 1298       G_UNLOCK(auth_mutex);
 1299     }
 1300   return res;
 1301 }
 1302 
 1303 /* FIXME: optimize header processing a bit (no need to copy hdr into a
 1304    buffer) */
 1305 static gboolean
 1306 http_process_auth_info(HttpProxy *self, HttpHeader *h, ZorpAuthInfo *auth_info)
 1307 {
 1308   gchar userpass[128];
 1309   gchar *p;
 1310   gchar **up;
 1311 
 1312   z_proxy_enter(self);
 1313 
 1314   if (self->old_auth_header->len &&
 1315       strcmp(h->value->str, self->old_auth_header->str) == 0)
 1316     z_proxy_return(self, TRUE);
 1317 
 1318   if (strncmp(h->value->str, "Basic", 5) != 0)
 1319     {
 1320       /*LOG
 1321         This message indicates that the client tried to use the given
 1322         unsupported HTTP authentication. Currently only Basic HTTP
 1323         authentication is supported by Zorp.
 1324       */
 1325       z_proxy_log(self, HTTP_ERROR, 3, "Only Basic authentication is supported; authentication='%s'", h->value->str);
 1326       /* not basic auth */
 1327       z_proxy_return(self, FALSE);
 1328     }
 1329 
 1330   p = h->value->str + 5;
 1331 
 1332   while (*p == ' ')
 1333     p++;
 1334 
 1335   if (!http_decode_base64(userpass, sizeof(userpass), p, strlen(p)))
 1336     {
 1337       /*LOG
 1338         This message indicates that the client sent a malformed
 1339         username:password field, during the authentication phase.
 1340       */
 1341       z_proxy_log(self, HTTP_VIOLATION, 1, "Invalid base64 encoded username:password pair;");
 1342       z_proxy_return(self, FALSE);
 1343     }
 1344 
 1345   up = g_strsplit(userpass, ":", 2);
 1346 
 1347   if (up)
 1348     {
 1349       bool res = http_do_authenticate(self, auth_info, up[0], up[1]);
 1350       if (res)
 1351         g_string_assign(self->old_auth_header, h->value->str);
 1352 
 1353       g_strfreev(up);
 1354       z_proxy_return(self, res);
 1355     }
 1356 
 1357   /*LOG
 1358     This message indicates that the username:password field received
 1359     during authentication was malformed.
 1360   */
 1361   z_proxy_log(self, HTTP_VIOLATION, 2, "No colon is found in the decoded username:password pair;");
 1362   z_proxy_return(self, FALSE);
 1363 }
 1364 
 1365 static gboolean
 1366 http_rewrite_host_header(HttpProxy *self, gchar *host, gint host_len, guint port, guint server_protocol)
 1367 {
 1368   HttpHeader *h;
 1369   guint rfc_port = 80;
 1370 
 1371   z_proxy_enter(self);
 1372 
 1373   if (server_protocol == HTTP_PROTO_HTTPS)
 1374     rfc_port = 443;
 1375 
 1376   if (self->rewrite_host_header && http_lookup_header(&self->headers[EP_CLIENT], "Host", &h))
 1377     {
 1378       /* NOTE: the constant rfc_port below is intentional, if
 1379        * default_port is changed we still have to send
 1380        * correct host header (containing port number)
 1381        */
 1382       if (port != rfc_port && port != 0)
 1383         g_string_sprintf(h->value, "%.*s:%d", host_len, host, port);
 1384       else
 1385         g_string_sprintf(h->value, "%.*s", host_len, host);
 1386     }
 1387 
 1388   z_proxy_return(self, TRUE);
 1389 }
 1390 
 1391 static gboolean
 1392 http_format_request(HttpProxy *self, gboolean stacked, GString *request)
 1393 {
 1394   gboolean res = TRUE;
 1395   const gchar *reason;
 1396   GString *url = g_string_sized_new(self->request_url->len);
 1397 
 1398   z_proxy_enter(self);
 1399 
 1400   if (self->proto_version[EP_CLIENT] >= 0x0100)
 1401     {
 1402       if (self->request_flags & HTTP_REQ_FLG_CONNECT)
 1403         {
 1404           g_assert(http_parent_proxy_enabled(self));
 1405 
 1406           http_flat_headers(&self->headers[EP_CLIENT]);
 1407           g_string_sprintf(request, "%s %s %s\r\n%s\r\n",
 1408                            self->request_method->str, self->request_url->str, self->request_version,
 1409                            self->headers[EP_CLIENT].flat->str);
 1410         }
 1411       else
 1412         {
 1413           if (!stacked)
 1414             {
 1415               http_flat_headers(&self->headers[EP_CLIENT]);
 1416 
 1417               if (!http_format_url(&self->request_url_parts, url, http_parent_proxy_enabled(self), self->permit_unicode_url, self->use_canonicalized_urls, &reason))
 1418                 res = FALSE;
 1419 
 1420 #define g_string_append_gstring(r, s) g_string_append_len(r, s->str, s->len)
 1421 #define g_string_append_const(r, s) g_string_append_len(r, s, __builtin_strlen(s))
 1422 
 1423               /* slightly less than a page to make sure it fits on a page even with the malloc overhead */
 1424               g_string_set_size(request, 4000);
 1425               g_string_truncate(request, 0);
 1426               g_string_append_gstring(request, self->request_method);
 1427               g_string_append_c(request, ' ');
 1428               g_string_append_gstring(request, url);
 1429               g_string_append_c(request, ' ');
 1430               g_string_append(request, self->request_version);
 1431               g_string_append_const(request, "\r\n");
 1432               g_string_append_gstring(request, self->headers[EP_CLIENT].flat);
 1433               g_string_append_const(request, "\r\n");
 1434             }
 1435           else
 1436             {
 1437               http_flat_headers_into(&self->headers[EP_CLIENT], request);
 1438             }
 1439         }
 1440     }
 1441   else
 1442     {
 1443       if (!http_format_url(&self->request_url_parts, url, FALSE, self->permit_unicode_url, self->use_canonicalized_urls, &reason))
 1444         res = FALSE;
 1445       else
 1446         g_string_sprintf(request, "%s %s\r\n", self->request_method->str, url->str);
 1447     }
 1448 
 1449   if (!res)
 1450     z_proxy_log(self, HTTP_ERROR, 3, "Error reformatting requested URL; url='%s', reason='%s'", self->request_url->str, reason);
 1451 
 1452   g_string_free(url, TRUE);
 1453   z_proxy_return(self, res);
 1454 }
 1455 
 1456 static gboolean
 1457 http_format_early_request(HttpProxy *self, gboolean stacked, GString *preamble)
 1458 {
 1459   if (stacked)
 1460     return http_format_request(self, stacked, preamble);
 1461   else
 1462     g_string_assign(preamble, "");
 1463 
 1464   return TRUE;
 1465 }
 1466 
 1467 bool
 1468 http_query_string_get_value(const std::string &stream_line, const std::string &key, std::string &value)
 1469 {
 1470   std::string::size_type start_from = 0;
 1471 
 1472   while (start_from != std::string::npos)
 1473     {
 1474       const std::string::size_type equal_pos = stream_line.find("=", start_from);
 1475       const std::string::size_type and_pos = stream_line.find('&', equal_pos + 1);
 1476 
 1477       if (equal_pos == std::string::npos)
 1478         {
 1479           return false;
 1480         }
 1481 
 1482       if (!stream_line.compare(start_from, equal_pos - start_from, key))
 1483         {
 1484           std::string::size_type value_len = and_pos;
 1485 
 1486           if (and_pos != std::string::npos)
 1487             {
 1488               value_len = and_pos - equal_pos - 1;
 1489             }
 1490 
 1491           value = stream_line.substr(equal_pos + 1, value_len);
 1492           return true;
 1493         }
 1494 
 1495       start_from = and_pos;
 1496       if (and_pos != std::string::npos)
 1497         start_from++;
 1498     }
 1499 
 1500   return false;
 1501 }
 1502 
 1503 static bool
 1504 http_form_auth_get_item(HttpProxy *self, const std::string &stream_line, const std::string &key, std::string &value,
 1505                         const bool mandatory = false)
 1506 {
 1507   if (!http_query_string_get_value(stream_line, key, value))
 1508     {
 1509       if (mandatory)
 1510         z_proxy_log(self, HTTP_ERROR, 3, "HTTP auth form's reply does not contain a mandatory input item; inputname='%s'", key.c_str());
 1511       else
 1512         z_proxy_log(self, HTTP_INFO, 3, "HTTP auth form's reply does not contain an optional input item, "
 1513                                         "but the request processing can continue; inputname='%s'", key.c_str());
 1514 
 1515       return false;
 1516     }
 1517 
 1518   const gchar *reason;
 1519   GString *decoded = g_string_new("");
 1520   if (http_string_assign_form_url_decode(decoded, TRUE, value.c_str(), value.length(), &reason))
 1521     {
 1522       value = decoded->str;
 1523     }
 1524   g_string_free(decoded, TRUE);
 1525 
 1526   return true;
 1527 }
 1528 
 1529 static bool
 1530 http_form_auth_get_username_password_redirect(HttpProxy *self, const std::string &stream_line, std::string &username,
 1531                                               std::string &password, std::string &redirect_location)
 1532 {
 1533   if (!http_form_auth_get_item(self, stream_line, "username", username, true) ||
 1534       !http_form_auth_get_item(self, stream_line, "password", password, true))
 1535     return false;
 1536 
 1537   http_form_auth_get_item(self, stream_line, "redirect_location", redirect_location, false);
 1538   return true;
 1539 }
 1540 
 1541 static std::string
 1542 http_form_auth_get_first_post_line(HttpProxy *self)
 1543 {
 1544   std::string post_line;
 1545 
 1546   std::unique_ptr<ZBlob, std::function<void(ZBlob*)>> blob(
 1547     z_blob_new(nullptr, 0),
 1548     std::bind(&z_blob_unref, std::placeholders::_1)
 1549   );
 1550   if (!blob)
 1551     return post_line;
 1552 
 1553   std::string session_id = std::string(self->super.session_id) + "/post";
 1554   std::unique_ptr<ZStream, std::function<void(ZStream*)>> blob_stream(
 1555     z_stream_blob_new(blob.get(), session_id.c_str()),
 1556     std::bind(&z_stream_unref, std::placeholders::_1)
 1557   );
 1558   blob_stream->timeout = -1;
 1559   if (!http_data_transfer(self, HTTP_TRANSFER_TO_BLOB, EP_CLIENT, self->super.endpoints[EP_CLIENT], EP_SERVER, blob_stream.get(), false, false, http_format_early_request))
 1560     {
 1561       return post_line;
 1562     }
 1563 
 1564   std::unique_ptr<ZStream, std::function<void(ZStream*)>> stream_line(
 1565     z_stream_push(z_stream_blob_new(blob.get(), session_id.c_str()), z_stream_line_new(nullptr, self->max_line_length, ZRL_EOL_CRLF | ZRL_PARTIAL_READ)),
 1566     [] (ZStream *stream) {
 1567       ZStream *tmpstream = z_stream_pop(stream);
 1568       z_stream_unref(tmpstream);
 1569     }
 1570   );
 1571 
 1572   gchar *line;
 1573   gsize line_length;
 1574   bool res = z_stream_line_get(stream_line.get(), &line, &line_length, nullptr);
 1575   if (line_length == 0)
 1576     return post_line;
 1577 
 1578   post_line = std::string(line).substr(0, line_length);
 1579 
 1580   return post_line;
 1581 }
 1582 
 1583 static void
 1584 http_form_auth_set_answer(HttpProxy *self, const std::string &redirect_location)
 1585 {
 1586   if (redirect_location.empty())
 1587     {
 1588       self->error_code = HTTP_MSG_OK;
 1589       self->error_status = 200;
 1590       g_string_assign(self->custom_response_body, "You are now authorized.");
 1591     }
 1592   else
 1593     {
 1594       self->error_code = HTTP_MSG_REDIRECT;
 1595       self->error_status = 301;
 1596       g_string_assign(self->error_info, redirect_location.c_str());
 1597       g_string_sprintfa(self->error_headers, "Location: %s\r\n", redirect_location.c_str());
 1598     }
 1599   self->send_custom_response = TRUE;
 1600 }
 1601 
 1602 static bool
 1603 http_get_form_auth(HttpProxy *self, ZorpAuthInfo *auth_info, std::string &redirect_location)
 1604 {
 1605   HttpHeader *header, *transfer_encoding_header;
 1606 
 1607   if (!(self->request_flags & HTTP_REQ_FLG_POST))
 1608     {
 1609       z_proxy_log(self, HTTP_ERROR, 3, "HTTP auth form reply's method is not POST;");
 1610       return false;
 1611     }
 1612 
 1613   if (!http_lookup_header(&self->headers[EP_CLIENT], "Transfer-Encoding", &transfer_encoding_header) &&
 1614       !http_lookup_header(&self->headers[EP_CLIENT], "Content-Length", &header))
 1615     {
 1616       z_proxy_log(self, HTTP_ERROR, 3, "HTTP auth form reply's does not contain either "
 1617                                        "Content-Length or Transfer-Encoding header;");
 1618       return false;
 1619     }
 1620 
 1621   if (transfer_encoding_header &&
 1622       (strcasecmp(transfer_encoding_header->value->str, "chunked") &&
 1623        strcasecmp(transfer_encoding_header->value->str, "identity")))
 1624     {
 1625       z_proxy_log(self, HTTP_ERROR, 3, "HTTP auth form reply's Transfer-Encoding is not supported, "
 1626                                        "only chunked and identity are allowed; transfer_encoding='%s'",
 1627                                        transfer_encoding_header->value->str);
 1628       return false;
 1629     }
 1630 
 1631   const char *content_type = "application/x-www-form-urlencoded";
 1632   if (!http_lookup_header(&self->headers[EP_CLIENT], "Content-Type", &header) ||
 1633       strncmp(header->value->str, content_type, sizeof(content_type)-1))
 1634     {
 1635       z_proxy_log(self, HTTP_ERROR, 3, "HTTP auth form reply's Content-Type is not %s;", content_type);
 1636       return false;
 1637     }
 1638 
 1639   const std::string charset_delimeter = "charset=";
 1640   std::string content_type_value(header->value->str);
 1641   std::string::size_type charset_pos = content_type_value.find(charset_delimeter);
 1642   if (charset_pos != std::string::npos)
 1643     {
 1644       std::string charset = content_type_value.substr(charset_pos + charset_delimeter.length());
 1645       std::transform(charset.begin(), charset.end(), charset.begin(),
 1646                      [](unsigned char c){ return std::toupper(c); });
 1647 
 1648       if (charset.find("UTF-8") == std::string::npos &&
 1649           charset.find("US-ASCII") == std::string::npos)
 1650         {
 1651           z_proxy_log(self, HTTP_ERROR, 3, "HTTP auth form reply's charset is not supported; charset='%s'", charset.c_str());
 1652           return false;
 1653         }
 1654     }
 1655 
 1656   std::string post_line = http_form_auth_get_first_post_line(self);
 1657   if (post_line.empty())
 1658     return false;
 1659 
 1660   std::string username, password;
 1661   if (!http_form_auth_get_username_password_redirect(self, post_line, username, password, redirect_location))
 1662     return false;
 1663 
 1664   bool res = http_do_authenticate(self, auth_info, username, password);
 1665   if (res)
 1666     {
 1667       http_form_auth_set_answer(self, redirect_location);
 1668     }
 1669 
 1670   return res;
 1671 }
 1672 
 1673 static gboolean
 1674 http_fetch_request(HttpProxy *self)
 1675 {
 1676   gchar *line;
 1677   gsize line_length;
 1678   gint empty_lines = 0;
 1679   guint res;
 1680 
 1681   z_proxy_enter(self);
 1682   /* FIXME: this can probably be removed as http_fetch_header does this */
 1683   http_clear_headers(&self->headers[EP_CLIENT]);
 1684 
 1685   while (empty_lines < HTTP_MAX_EMPTY_REQUESTS)
 1686     {
 1687       self->super.endpoints[EP_CLIENT]->timeout = self->timeout_request;
 1688       res = z_stream_line_get(self->super.endpoints[EP_CLIENT], &line, &line_length, NULL);
 1689       self->super.endpoints[EP_CLIENT]->timeout = self->timeout;
 1690 
 1691       if (res == G_IO_STATUS_EOF)
 1692         {
 1693           self->error_code = HTTP_MSG_OK;
 1694           z_proxy_return(self, FALSE);
 1695         }
 1696 
 1697       if (res != G_IO_STATUS_NORMAL)
 1698         {
 1699           self->error_code = HTTP_MSG_OK;
 1700 
 1701           if (errno == ETIMEDOUT)
 1702             {
 1703               if (self->request_count == 0)
 1704                 {
 1705                   self->error_code = HTTP_MSG_CLIENT_TIMEOUT;
 1706                   self->error_status = 408;
 1707                 }
 1708             }
 1709 
 1710           z_proxy_return(self, FALSE);
 1711         }
 1712 
 1713       if (line_length != 0)
 1714         break;
 1715 
 1716       empty_lines++;
 1717     }
 1718 
 1719   if (!http_split_request(self, line, line_length))
 1720     {
 1721       g_string_assign(self->error_info, "Invalid request line.");
 1722       /*LOG
 1723         This message indicates that the client sent an invalid HTTP request
 1724         to the proxy.
 1725       */
 1726       z_proxy_log(self, HTTP_VIOLATION, 2, "Invalid HTTP request received; line='%.*s'", (gint)line_length, line);
 1727       z_proxy_return(self, FALSE);
 1728     }
 1729 
 1730   self->request_flags = http_proto_request_lookup(self->request_method->str);
 1731 
 1732   if (!http_parse_version(self, EP_CLIENT, self->request_version))
 1733     z_proxy_return(self, FALSE); /* parse version already logged */
 1734 
 1735   if (!http_fetch_headers(self, EP_CLIENT))
 1736     z_proxy_return(self, FALSE); /* fetch headers already logged */
 1737 
 1738   if (self->rerequest_attempts > 0)
 1739     {
 1740       HttpHeader *transfer_encoding_hdr, *content_length_hdr;
 1741       gboolean has_data = FALSE;
 1742 
 1743       if (http_lookup_header(&self->headers[EP_CLIENT], "Transfer-Encoding", &transfer_encoding_hdr))
 1744         has_data = TRUE;
 1745 
 1746       if (http_lookup_header(&self->headers[EP_CLIENT], "Content-Length", &content_length_hdr))
 1747         has_data = TRUE;
 1748 
 1749       self->request_data_stored = TRUE;
 1750 
 1751       if (self->request_data)
 1752         z_blob_truncate(self->request_data, 0, -1);
 1753 
 1754       if (has_data)
 1755         {
 1756           gchar session_id[MAX_SESSION_ID];
 1757           ZStream *blob_stream;
 1758           gboolean success;
 1759 
 1760           if (!self->request_data)
 1761             self->request_data = z_blob_new(NULL, 0);
 1762 
 1763           if (!self->request_data)
 1764             z_proxy_return(self, FALSE);
 1765 
 1766           g_snprintf(session_id, sizeof(session_id), "%s/post", self->super.session_id);
 1767           blob_stream = z_stream_blob_new(self->request_data, session_id);
 1768           blob_stream->timeout = -1;
 1769           /* fetch data associated with request */
 1770           success = http_data_transfer(self, HTTP_TRANSFER_TO_BLOB, EP_CLIENT, self->super.endpoints[EP_CLIENT], EP_SERVER, blob_stream, FALSE, FALSE, http_format_early_request);
 1771           z_stream_unref(blob_stream);
 1772 
 1773           if (!success)
 1774             z_proxy_return(self, FALSE);
 1775         }
 1776     }
 1777 
 1778   z_proxy_return(self, TRUE);
 1779 }
 1780 
 1781 static gboolean
 1782 http_auth_is_expired(gpointer  /* key */, gpointer value, gpointer user_data)
 1783 {
 1784   ZorpAuthInfo *real_value = static_cast<ZorpAuthInfo *>(value);
 1785   time_t max_time = MAX(MAX(real_value->last_auth_time, real_value->accept_credit_until), real_value->created_at);
 1786   time_t cut_time = GPOINTER_TO_UINT(user_data);
 1787 
 1788   return (max_time < cut_time);
 1789 }
 1790 
 1791 static void
 1792 http_auth_destroy(gpointer value)
 1793 {
 1794   ZorpAuthInfo *real_value = static_cast<ZorpAuthInfo *>(value);
 1795 
 1796   g_free(real_value->username);
 1797   g_strfreev(real_value->groups);
 1798   g_free(real_value->basic_auth_creds);
 1799   g_free(real_value);
 1800 }
 1801 
 1802 static inline gchar *
 1803 http_process_create_realm(HttpProxy *self, time_t now, gchar *buf, guint buflen)
 1804 {
 1805   if (self->max_auth_time > 0)
 1806     g_snprintf(buf, buflen, "Basic realm=\"%s (id:%x)\"", self->auth_realm->str, (int)now);
 1807   else
 1808     g_snprintf(buf, buflen, "Basic realm=\"%s\"", self->auth_realm->str);
 1809 
 1810   return buf;
 1811 }
 1812 
 1813 
 1814 static gboolean
 1815 http_extract_client_info_from_cookies(HttpProxy *self, gchar *key, const guint key_length)
 1816 {
 1817   CookiePairVector cookie_vector = http_parse_header_cookie(&self->headers[EP_CLIENT]);
 1818   if (! cookie_vector.empty())
 1819     {
 1820       // FIXME: for now only the first matching cookie will be used
 1821       CookiePairVector::iterator cookie_pair = http_find_cookie_by_name(cookie_vector, HttpProxy::zorp_realm_cookie_name);
 1822       if (cookie_pair != cookie_vector.end())
 1823         {
 1824           g_strlcpy(key, cookie_pair->second.c_str(), key_length);
 1825 
 1826           // removing the Zorp auth cookie, because of security reasons (information leaking)
 1827           cookie_vector.erase(cookie_pair);
 1828 
 1829           http_write_header_cookie(&self->headers[EP_CLIENT], cookie_vector);
 1830 
 1831           return TRUE;
 1832         }
 1833     }
 1834 
 1835   return FALSE;
 1836 }
 1837 
 1838 static gboolean
 1839 http_generate_client_info(gchar *key, const guint key_length)
 1840 {
 1841   guchar raw_key[32];
 1842 
 1843   if (!z_random_sequence_get(Z_RANDOM_STRONG, raw_key, sizeof(raw_key)))
 1844     return FALSE;
 1845 
 1846   ZCode * coder = z_code_base64_encode_new(256, 255);
 1847   if (coder == NULL)
 1848     return FALSE;
 1849 
 1850   if (!z_code_transform(coder, raw_key, sizeof(raw_key)) ||
 1851       !z_code_finish(coder))
 1852     return FALSE;
 1853 
 1854   gsize pos = z_code_get_result(coder, key, key_length - 1);
 1855   key[pos-2] = 0;
 1856 
 1857   return TRUE;
 1858 }
 1859 
 1860 static gboolean
 1861 http_get_client_info(HttpProxy *self, gchar *key, const guint key_length)
 1862 {
 1863   if (http_extract_client_info_from_cookies(self, key, key_length) ||
 1864       http_generate_client_info(key, key_length))
 1865     return TRUE;
 1866 
 1867   return FALSE;
 1868 }
 1869 
 1870 static gboolean
 1871 http_check_name(HttpProxy *self)
 1872 {
 1873   z_proxy_enter(self);
 1874   ZProxyHostIface *host_iface = Z_CAST(z_proxy_find_iface(&self->super, Z_CLASS(ZProxyHostIface)), ZProxyHostIface);
 1875 
 1876   if (host_iface == NULL)
 1877     host_iface = Z_CAST(z_proxy_find_iface(self->super.parent_proxy, Z_CLASS(ZProxyHostIface)), ZProxyHostIface);
 1878 
 1879   if (host_iface)
 1880     {
 1881       gchar error_reason[256];
 1882 
 1883       if (!z_proxy_host_iface_check_name(host_iface, self->remote_server->str, error_reason, sizeof(error_reason)))
 1884         {
 1885           z_proxy_log(self, HTTP_ERROR, 3, "Error checking hostname; error='%s'", error_reason);
 1886           g_string_assign(self->error_info, error_reason);
 1887           z_proxy_return(self, FALSE);
 1888         }
 1889 
 1890       z_object_unref(&host_iface->super);
 1891     }
 1892 
 1893   z_proxy_return(self, TRUE);
 1894 }
 1895 
 1896 static gint
 1897 http_get_default_port_for_protocol(HttpProxy *self)
 1898 {
 1899   if (self->server_protocol == HTTP_PROTO_HTTP)
 1900     return self->default_http_port;
 1901   else if (self->server_protocol == HTTP_PROTO_HTTPS)
 1902     return self->default_https_port;
 1903   else
 1904     return self->default_ftp_port;
 1905 }
 1906 
 1907 
 1908 static guint
 1909 http_get_request_type(HttpProxy *self)
 1910 {
 1911   guint request_type;
 1912 
 1913   if (self->proto_version[EP_CLIENT] < 0x0100)
 1914     {
 1915       /* no proxy protocol for version 0.9 */
 1916       request_type = HTTP_REQTYPE_SERVER;
 1917     }
 1918   else
 1919     {
 1920       if (self->request_url->str[0] == '/')
 1921         request_type = HTTP_REQTYPE_SERVER;
 1922       else
 1923         request_type = HTTP_REQTYPE_PROXY;
 1924     }
 1925 
 1926   return request_type;
 1927 }
 1928 
 1929 static HttpHeader *
 1930 http_add_proxy_connection_header_as_connection_header(HttpProxy *self, HttpHeader *pconn_hdr)
 1931 {
 1932   gchar header_name[] = "Connection";
 1933   guint header_name_size = strlen(header_name);
 1934   HttpHeader *conn_hdr = http_add_header(&self->headers[EP_CLIENT],
 1935                                          header_name, header_name_size,
 1936                                          pconn_hdr->value->str, pconn_hdr->value->len);
 1937   return conn_hdr;
 1938 }
 1939 
 1940 static gboolean
 1941 http_process_proxy_connection_header(HttpProxy *self)
 1942 {
 1943   HttpHeader *pconn_hdr;
 1944   HttpHeader *conn_hdr;
 1945 
 1946   gboolean has_proxy_conn_hdr = http_lookup_header(&self->headers[EP_CLIENT], "Proxy-Connection", &pconn_hdr);
 1947   if (has_proxy_conn_hdr)
 1948     {
 1949       gboolean is_proxy_connection_header_possible = (self->proto_version[EP_CLIENT] >= 0x0100);
 1950       if (!is_proxy_connection_header_possible)
 1951         has_proxy_conn_hdr = FALSE;
 1952 
 1953       pconn_hdr->present = FALSE;
 1954     }
 1955 
 1956   gboolean has_conn_hdr = http_lookup_header(&self->headers[EP_CLIENT], "Connection", &conn_hdr) &&
 1957                           conn_hdr->present;
 1958   gboolean has_proxy_connection_header_only = has_proxy_conn_hdr && !has_conn_hdr;
 1959   if (has_proxy_connection_header_only)
 1960     self->connection_hdr = http_add_proxy_connection_header_as_connection_header(self, pconn_hdr);
 1961   else if (has_conn_hdr)
 1962     self->connection_hdr = conn_hdr;
 1963 
 1964   return (self->connection_hdr != NULL);
 1965 }
 1966 
 1967 static void
 1968 http_process_connection_headers(HttpProxy *self)
 1969 {
 1970   self->connection_hdr = NULL;
 1971   http_process_proxy_connection_header(self);
 1972 }
 1973 
 1974 static void
 1975 http_iterate_connection_header_tokens(HttpHeaders *headers, HttpHeader *conn_hdr)
 1976 {
 1977   const gchar *linear_white_spaces = " \t,";
 1978   gchar **conn_hdr_tokens = g_strsplit_set(conn_hdr->value->str, linear_white_spaces, -1);
 1979   GString *conn_hdr_new_value = g_string_sized_new(conn_hdr->value->len);
 1980   for (gchar **conn_hdr_token = conn_hdr_tokens;
 1981        *conn_hdr_token != NULL;
 1982        conn_hdr_token++)
 1983     {
 1984       if (**conn_hdr_token == '\0')
 1985         continue;
 1986 
 1987       HttpHeader *token_hdr;
 1988       if (http_lookup_header(headers, *conn_hdr_token, &token_hdr))
 1989         {
 1990           token_hdr->present = FALSE;
 1991         }
 1992       else
 1993         {
 1994           if (conn_hdr_new_value->len)
 1995             g_string_append_c(conn_hdr_new_value, ' ');
 1996           g_string_append(conn_hdr_new_value, *conn_hdr_token);
 1997         }
 1998     }
 1999   g_strfreev(conn_hdr_tokens);
 2000 
 2001   gboolean is_new_connection_hdr_empty = (conn_hdr_new_value->len == 0);
 2002   if (is_new_connection_hdr_empty)
 2003     conn_hdr->present = FALSE;
 2004   else
 2005     g_string_assign(conn_hdr->value, conn_hdr_new_value->str);
 2006 
 2007   g_string_free(conn_hdr_new_value, TRUE);
 2008 }
 2009 
 2010 static void
 2011 http_process_connection_header_tokens(HttpHeaders *headers, HttpHeader *conn_hdr)
 2012 {
 2013   http_iterate_connection_header_tokens(headers, conn_hdr);
 2014 }
 2015 
 2016 static void
 2017 http_form_auth_replace_redirect_location(HttpProxy *self, std::string &html_form, const std::string &redirect_location)
 2018 {
 2019   std::string search_string {"\"redirect_location\" value=\"\""};
 2020   std::size_t found = html_form.find(search_string);
 2021   if (found != std::string::npos)
 2022     {
 2023       std::string request_url(redirect_location);
 2024       if (request_url.empty())
 2025         {
 2026           if (self->remote_server->len)
 2027             request_url = std::string(self->super.tls_opts.ssl_sessions[EP_CLIENT] ? "https" : "http") +
 2028                           "://" + self->remote_server->str;
 2029           request_url.append(self->request_url->str);
 2030         }
 2031       html_form.insert(found+search_string.length()-1, request_url);
 2032     }
 2033 }
 2034 
 2035 static void
 2036 http_auth_update_accept_credit_until(HttpProxy *self, ZorpAuthInfo *auth_info, const time_t &now)
 2037 {
 2038   G_LOCK(auth_mutex);
 2039 
 2040   if (self->max_auth_time > 0)
 2041     auth_info->accept_credit_until = now + self->max_auth_time;
 2042 
 2043   G_UNLOCK(auth_mutex);
 2044 }
 2045 
 2046 static void
 2047 http_set_response(HttpProxy *self, int error_code, unsigned int error_status)
 2048 {
 2049   self->error_code = error_code;
 2050   self->error_status = error_status;
 2051 }
 2052 
 2053 static void
 2054 http_add_authorization_hdr(HttpProxy *self, ZorpAuthInfo *auth_info)
 2055 {
 2056   if (self->transparent_mode && (self->auth_by_form || self->auth_by_cookie))
 2057     {
 2058       z_proxy_log(self, HTTP_POLICY, 4, "Relaying Basic authentication after successful client "
 2059                                         "authentication; username='%s'", auth_info->username);
 2060       http_add_header(&self->headers[EP_CLIENT], "Authorization", strlen("Authorization"),
 2061                       self->auth_header_value->str, self->auth_header_value->len);
 2062     }
 2063   else if (!self->transparent_mode && self->auth_by_cookie)
 2064     {
 2065       z_proxy_log(self, HTTP_POLICY, 4, "Relaying Basic proxy-authentication after successful client "
 2066                                         "authentication; username='%s'", auth_info->username);
 2067       http_add_header(&self->headers[EP_CLIENT], "Proxy-Authorization", strlen("Proxy-Authorization"),
 2068                       self->auth_header_value->str, self->auth_header_value->len);
 2069     }
 2070 }
 2071 
 2072 static gboolean
 2073 http_process_request(HttpProxy *self)
 2074 {
 2075   HttpHeader *host_header = nullptr, *authorization_header = nullptr;
 2076   const gchar *reason;
 2077   static time_t prev_cleanup = 0;
 2078   ZorpAuthInfo *auth_info;
 2079 
 2080   /* The variable below is to keep the code simple.
 2081      There are some situation when Zorp should put
 2082      a Set-Cookie header into the answer. But the
 2083      condition is not too simple. And the good place
 2084      when all the condititons could evaluate esaily is
 2085      too early to get server domain name which is
 2086      needed for the header. So the header should be set
 2087      in a latter place. This variable is to know that this
 2088      should happen. */
 2089   gboolean need_cookie_header = FALSE;
 2090   gchar client_key[512] = "";
 2091 
 2092   z_proxy_enter(self);
 2093 
 2094   if (self->proto_version[EP_CLIENT] > 0x0100)
 2095     self->connection_mode = HTTP_CONNECTION_KEEPALIVE;
 2096   else
 2097     self->connection_mode = HTTP_CONNECTION_CLOSE;
 2098 
 2099   if (http_lookup_header(&self->headers[EP_CLIENT], "Host", &host_header))
 2100     g_string_assign(self->remote_server, host_header->value->str);
 2101   else
 2102     g_string_truncate(self->remote_server, 0);
 2103 
 2104   // please mind, that auth can be set to NULL in case of keep-alive connections further requests!
 2105   if (self->auth)
 2106     {
 2107       if (self->proto_version[EP_CLIENT] >= 0x0100)
 2108         {
 2109           ZSockAddr *client_addr;
 2110           struct in_addr c_addr;
 2111           time_t now = time(NULL);
 2112           gchar buf[4096];
 2113 
 2114           bool basic_auth_header_exists = http_lookup_header(&self->headers[EP_CLIENT], "Authorization", &authorization_header);
 2115           bool do_basic_auth = basic_auth_header_exists || !self->auth_by_form;
 2116 
 2117           if (self->auth_by_cookie || !do_basic_auth)
 2118             {
 2119               if (!http_get_client_info(self, client_key, sizeof(client_key)))
 2120                 g_assert_not_reached();
 2121             }
 2122           else if (basic_auth_header_exists)
 2123             {
 2124               z_proxy_get_addresses(&self->super, NULL, &client_addr, NULL, NULL, NULL, NULL);
 2125               c_addr = z_sockaddr_inet_get_address(client_addr);
 2126               z_sockaddr_unref(client_addr);
 2127               z_inet_ntoa(client_key, INET_ADDRSTRLEN, c_addr);
 2128               strncat(client_key, authorization_header->value->str, authorization_header->value->len);
 2129             }
 2130 
 2131           G_LOCK(auth_mutex);
 2132 
 2133           if (now > prev_cleanup + (2 * MAX(self->max_auth_time, self->auth_cache_time)))
 2134             {
 2135               g_hash_table_foreach_remove(auth_hash, http_auth_is_expired,
 2136                                           GUINT_TO_POINTER(now - (2 * MAX(self->max_auth_time, self->auth_cache_time))));
 2137               prev_cleanup = now;
 2138             }
 2139 
 2140           auth_info = static_cast<ZorpAuthInfo *>(g_hash_table_lookup(auth_hash, client_key));
 2141 
 2142           bool auth_info_should_be_freed = false;
 2143           if (auth_info == NULL)
 2144             {
 2145               auth_info = g_new0(ZorpAuthInfo, 1);
 2146               auth_info->created_at = now;
 2147               auth_info_should_be_freed = true;
 2148               if (client_key[0] != '\0')
 2149                 {
 2150                   g_hash_table_insert(auth_hash, g_strdup(client_key), auth_info);
 2151                   auth_info_should_be_freed = false;
 2152                 }
 2153             }
 2154 
 2155           bool is_auth_cache_not_expired = self->auth_cache_time > 0 &&
 2156                                            self->auth_cache_time + auth_info->last_auth_time > now;
 2157 
 2158           bool user_authenticated = is_auth_cache_not_expired &&
 2159                                     z_proxy_user_authenticated_default(&self->super, auth_info->username,
 2160                                                                        const_cast<const gchar **>(auth_info->groups));
 2161 
 2162           z_proxy_log(self, HTTP_DEBUG, 7, "User authentication cache state; entity='%s', state='%d'", auth_info->username, is_auth_cache_not_expired);
 2163 
 2164           if (user_authenticated)
 2165             {
 2166               if (self->auth_cache_update)
 2167                 {
 2168                   auth_info->last_auth_time = now;
 2169                 }
 2170 
 2171               if (self->auth_forward && auth_info->basic_auth_creds)
 2172                 {
 2173                   g_string_assign(self->auth_header_value, auth_info->basic_auth_creds);
 2174 
 2175                   if (self->auth_by_cookie || (self->auth_by_form && !basic_auth_header_exists))
 2176                     {
 2177                       http_add_authorization_hdr(self, auth_info);
 2178                     }
 2179                 }
 2180               G_UNLOCK(auth_mutex);
 2181             }
 2182           else
 2183             {
 2184               /* authentication is required */
 2185               g_string_truncate(self->auth_header_value, 0);
 2186               G_UNLOCK(auth_mutex);
 2187 
 2188               bool is_auth_time_window_expired = auth_info->accept_credit_until > 0 && auth_info->accept_credit_until < now;
 2189               if (self->transparent_mode)
 2190                 {
 2191                   std::string redirect_location;
 2192                   if (!do_basic_auth && (is_auth_time_window_expired || !http_get_form_auth(self, auth_info, redirect_location)))
 2193                     {
 2194                       http_auth_update_accept_credit_until(self, auth_info, now);
 2195                       http_set_response(self, HTTP_MSG_OK, 200);
 2196 
 2197                       std::string authform_file_path;
 2198                       if (self->login_page_path->len > 0)
 2199                         authform_file_path = self->login_page_path->str;
 2200                       else
 2201                         authform_file_path = http_error_file_path(self, "authform.html");
 2202 
 2203                       gchar *authform_file = z_error_loader_format_file(authform_file_path.c_str(), "", Z_EF_ESCAPE_HTML, NULL, NULL);
 2204                       if (authform_file)
 2205                         {
 2206                           std::string html_form {authform_file};
 2207                           http_form_auth_replace_redirect_location(self, html_form, redirect_location);
 2208                           g_string_assign(self->custom_response_body, html_form.c_str());
 2209                           g_free(authform_file);
 2210                           self->send_custom_response = TRUE;
 2211                         }
 2212                       else
 2213                         {
 2214                           self->error_code = HTTP_MSG_IO_ERROR;
 2215                         }
 2216                       z_proxy_return(self, FALSE);
 2217                     }
 2218                   else if (do_basic_auth && (is_auth_time_window_expired || !basic_auth_header_exists || !http_process_auth_info(self, authorization_header, auth_info)))
 2219                     {
 2220                       http_auth_update_accept_credit_until(self, auth_info, now);
 2221                       http_set_response(self, HTTP_MSG_AUTH_REQUIRED, 401);
 2222                       g_string_sprintf(self->error_msg, "Authentication is required.");
 2223                       g_string_sprintfa(self->error_headers, "WWW-Authenticate: %s\r\n",
 2224                                         http_process_create_realm(self, now, buf, sizeof(buf)));
 2225                       z_proxy_return(self, FALSE);
 2226                     }
 2227                 }
 2228               else if (is_auth_time_window_expired ||
 2229                        !http_lookup_header(&self->headers[EP_CLIENT], "Proxy-Authorization", &authorization_header) ||
 2230                        !http_process_auth_info(self, authorization_header, auth_info))
 2231                 {
 2232                   if (self->auth_by_form)
 2233                     {
 2234                       z_proxy_log(self, HTTP_POLICY, 1, "Configuration error, form-based authentication "
 2235                                                         "is not supported in non-transparent mode; "
 2236                                                         "auth_by_form='%s', transparent_mode='%s'",
 2237                                                         self->auth_by_form ? "TRUE" : "FALSE",
 2238                                                         self->transparent_mode ? "TRUE" : "FALSE");
 2239                       self->error_code = HTTP_MSG_POLICY_SYNTAX;
 2240                       z_proxy_return(self, FALSE);
 2241                     }
 2242 
 2243                   http_auth_update_accept_credit_until(self, auth_info, now);
 2244                   http_set_response(self, HTTP_MSG_AUTH_REQUIRED, 407);
 2245                   g_string_sprintf(self->error_msg, "Authentication is required.");
 2246                   g_string_sprintfa(self->error_headers, "Proxy-Authenticate: %s\r\n",
 2247                                     http_process_create_realm(self, now, buf, sizeof(buf)));
 2248                   z_proxy_return(self, FALSE);
 2249                 }
 2250 
 2251               if (authorization_header)
 2252                 g_string_assign(self->auth_header_value, authorization_header->value->str);
 2253 
 2254               if (self->auth_by_cookie || !do_basic_auth)
 2255                 need_cookie_header = TRUE;
 2256             }
 2257           if (auth_info_should_be_freed)
 2258             http_auth_destroy(auth_info);
 2259         }
 2260       else
 2261         {
 2262           z_proxy_log(self, HTTP_POLICY, 2, "Authentication required, but client requested HTTP/0.9 which does not support authentication;");
 2263           z_proxy_return(self, FALSE);
 2264         }
 2265     }
 2266 
 2267   if (self->request_flags & HTTP_REQ_FLG_CONNECT)
 2268     {
 2269       if (self->proto_version[EP_CLIENT] >= 0x0100)
 2270         {
 2271           self->request_type = HTTP_REQTYPE_PROXY;
 2272           z_proxy_return(self, TRUE);
 2273         }
 2274 
 2275       self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 2276       g_string_sprintf(self->error_info, "CONNECT method requires HTTP/1.0 or later");
 2277       /*LOG
 2278         This message indicates that the client sent a CONNECT method
 2279         request, but it is only supported for HTTP/1.0 or later.
 2280       */
 2281       z_proxy_log(self, HTTP_VIOLATION, 1, "CONNECT method without version specification;");
 2282       z_proxy_return(self, FALSE);
 2283     }
 2284 
 2285   self->request_type = http_get_request_type(self);
 2286   http_process_connection_headers(self);
 2287 
 2288   if (self->connection_hdr)
 2289     {
 2290       gint connection_mode;
 2291 
 2292       http_process_connection_header_tokens(&self->headers[EP_CLIENT], self->connection_hdr);
 2293 
 2294       /* connection_mode overridden by connection header */
 2295       connection_mode = http_parse_connection_hdr_value(self, self->connection_hdr);
 2296 
 2297       if (connection_mode != HTTP_CONNECTION_UNKNOWN)
 2298         self->connection_mode = connection_mode;
 2299     }
 2300 
 2301   if (self->transparent_mode &&
 2302       ((self->request_type == HTTP_REQTYPE_PROXY && !self->permit_proxy_requests) ||
 2303        (self->request_type == HTTP_REQTYPE_SERVER && !self->permit_server_requests)))
 2304     {
 2305       /* */
 2306       self->error_code = HTTP_MSG_POLICY_VIOLATION;
 2307       g_string_sprintf(self->error_info, "%s requests not permitted in transparent mode.",
 2308                        self->request_type == HTTP_REQTYPE_SERVER ? "server" : "proxy");
 2309       /*LOG
 2310         This message indicates that the client sent the given type request,
 2311         which is not permitted by the policy. Check the
 2312         permit_proxy_requests and the permit_server_requests attributes.
 2313       */
 2314       z_proxy_log(self, HTTP_POLICY, 2,
 2315                   "This request type is not permitted in transparent mode; request_type='%s'",
 2316                   self->request_type == HTTP_REQTYPE_SERVER ? "server" : "proxy");
 2317       z_proxy_return(self, FALSE);
 2318     }
 2319 
 2320   if (self->transparent_mode)
 2321     {
 2322       if (self->request_url->str[0] == '/')
 2323         {
 2324           gchar buf[self->remote_server->len + 32];
 2325 
 2326           /* no protocol description */
 2327           if (!self->remote_server->len)
 2328             {
 2329               if (!self->require_host_header)
 2330                 {
 2331                   /* no host header */
 2332                   /*LOG
 2333                     This message indicates that no Host header was sent by
 2334                     the client. As the content of the host header is used
 2335                     to reconstruct the requested URL, the request_url
 2336                     attribute will refer to a host named 'unknown'.
 2337                   */
 2338                   z_proxy_log(self, HTTP_VIOLATION, 4, "No host header in transparent request, 'unknown' is used instead;");
 2339                   g_string_assign(self->remote_server, "unknown");
 2340                 }
 2341               else
 2342                 {
 2343                   self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 2344 
 2345                   if (self->proto_version[EP_CLIENT] < 0x0100)
 2346                     {
 2347                       g_string_sprintf(self->error_info, "'Host:' header is required, and HTTP/0.9 can't transfer headers.");
 2348                       /*LOG
 2349                         This message indicates that an HTTP/0.9 request was
 2350                         sent by the client, and Host header is required by
 2351                         the policy, but HTTP/0.9 does not support headers.
 2352                         Check the require_host_header attribute.
 2353                       */
 2354                       z_proxy_log(self, HTTP_POLICY, 2, "'Host:' header is required, and HTTP/0.9 can't transfer headers;");
 2355                     }
 2356                   else
 2357                     {
 2358                       g_string_sprintf(self->error_info, "No 'Host:' header in request, and policy requires this.");
 2359                       /*LOG
 2360                         This message indicates that no Host header was sent
 2361                         by the client, but it was required by the policy.
 2362                         Check the require_host_header attribute.
 2363                       */
 2364                       z_proxy_log(self, HTTP_POLICY, 2, "No 'Host:' header in request, and policy requires this;");
 2365                     }
 2366 
 2367                   z_proxy_return(self, FALSE);
 2368                 }
 2369             }
 2370 
 2371           g_snprintf(buf, sizeof(buf), "%s://%s", self->super.tls_opts.ssl_sessions[EP_CLIENT] ? "https" : "http", self->remote_server->str);
 2372           g_string_prepend(self->request_url, buf);
 2373         }
 2374     }
 2375 
 2376   if (!http_parse_url(&self->request_url_parts, self->permit_unicode_url,
 2377                       self->permit_invalid_hex_escape, FALSE, self->request_url->str, &reason))
 2378     {
 2379       /* invalid URL */
 2380       self->error_code = HTTP_MSG_INVALID_URL;
 2381       g_string_sprintf(self->error_info, "Badly formatted url: %s", self->request_url->str);
 2382       /*LOG
 2383         This message indicates that there was an error parsing an already
 2384         canonicalized URL.  Please report this event to the BalaSys
 2385     Development Team (at devel@balasys.hu)
 2386       */
 2387       z_proxy_log(self, HTTP_ERROR, 1, "Error parsing URL; url='%s', reason='%s'", self->request_url->str, reason);
 2388       z_proxy_return(self, FALSE);
 2389     }
 2390 
 2391   if (!http_format_url(&self->request_url_parts, self->request_url, TRUE, self->permit_unicode_url, TRUE, &reason))
 2392     {
 2393       self->error_code = HTTP_MSG_INVALID_URL;
 2394       g_string_sprintf(self->error_info, "Error canonicalizing url (%s): %s", reason, self->request_url->str);
 2395       /*LOG
 2396         This message indicates that there was an error parsing an already
 2397         canonicalized URL.  Please report this event to the BalaSys
 2398     Development Team (at devel@balasys.hu)
 2399       */
 2400       z_proxy_log(self, HTTP_ERROR, 1, "Error parsing URL; url='%s', reason='%s'", self->request_url->str, reason);
 2401       z_proxy_return(self, FALSE);
 2402     }
 2403 
 2404   self->remote_port = self->request_url_parts.port;
 2405 
 2406   if (need_cookie_header)
 2407     {
 2408       gchar *hostname = self->request_url_parts.host->str;
 2409       gchar *startpos = hostname;
 2410       gchar *dotpos = strchr(startpos, '.');
 2411       gchar *nextdotpos = NULL;
 2412 
 2413       if (dotpos != NULL)
 2414         nextdotpos = strchr(dotpos + 1, '.');
 2415 
 2416       while (nextdotpos != NULL)
 2417         {
 2418           startpos = dotpos;
 2419           dotpos = nextdotpos;
 2420           nextdotpos = strchr(dotpos + 1, '.');
 2421         }
 2422 
 2423       std::string append_cookie { HttpProxy::zorp_realm_cookie_name + "=" + client_key + "; path=/; domain=" + startpos};
 2424       if (self->auth_by_form)
 2425         {
 2426           g_string_append_printf(self->error_headers, "Set-Cookie: %s\r\n", append_cookie.c_str());
 2427           z_proxy_return(self, FALSE);
 2428         }
 2429       else
 2430         {
 2431           self->append_cookie = g_string_sized_new(32);
 2432           g_string_assign(self->append_cookie, append_cookie.c_str());
 2433         }
 2434     }
 2435 
 2436   z_proxy_return(self, TRUE);
 2437 }
 2438 
 2439 static gboolean
 2440 http_process_filtered_request(HttpProxy *self)
 2441 {
 2442   if ((self->request_flags & HTTP_REQ_FLG_CONNECT))
 2443     {
 2444       self->server_protocol = HTTP_PROTO_HTTP;
 2445     }
 2446   else if (http_parent_proxy_enabled(self) &&
 2447            (strcasecmp(self->request_url_parts.scheme->str, "http") == 0 ||
 2448             strcasecmp(self->request_url_parts.scheme->str, "ftp") == 0 ||
 2449             strcasecmp(self->request_url_parts.scheme->str, "cache_object") == 0))
 2450     {
 2451       self->server_protocol = HTTP_PROTO_HTTP;
 2452     }
 2453   else if (!http_parent_proxy_enabled(self) && strcasecmp(self->request_url_parts.scheme->str, "ftp") == 0)
 2454     {
 2455       if (self->permit_ftp_over_http)
 2456         {
 2457           self->server_protocol = HTTP_PROTO_FTP;
 2458         }
 2459       else
 2460         {
 2461           /*LOG
 2462             This message indicates that a client tried to use FTP over HTTP
 2463             which is not allowed by default. Either set a parent proxy or
 2464             enable the permit_ftp_over_http attribute.
 2465           */
 2466           z_proxy_log(self, HTTP_POLICY, 2, "Client attempted to use FTP over HTTP, which is currently disabled;");
 2467           self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 2468           z_proxy_return(self, FALSE);
 2469         }
 2470     }
 2471   else if (!http_parent_proxy_enabled(self) &&
 2472            strcasecmp(self->request_url_parts.scheme->str, "http") == 0)
 2473     {
 2474       self->server_protocol = HTTP_PROTO_HTTP;
 2475     }
 2476   else if (!http_parent_proxy_enabled(self) &&
 2477            strcasecmp(self->request_url_parts.scheme->str, "https") == 0)
 2478     {
 2479       self->server_protocol = HTTP_PROTO_HTTPS;
 2480     }
 2481   else
 2482     {
 2483       /* unsupported protocol */
 2484       self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 2485       g_string_sprintf(self->error_info, "Unsupported scheme: %s", self->request_url_parts.scheme->str);
 2486       /*LOG
 2487         This message indicates that the requested URL refers to an
 2488         unsupported protocol scheme. Zorp currently knows about http and
 2489         cache_object protocols, and can support the ftp protocol
 2490         if a parent_proxy supporting ftp over http tunneling is present.
 2491       */
 2492       z_proxy_log(self, HTTP_ERROR, 3, "Unsupported scheme in URL; proto='%s'", self->request_url_parts.scheme->str);
 2493       z_proxy_return(self, FALSE);
 2494     }
 2495 
 2496   if (self->request_url_parts.host->len > self->max_hostname_length)
 2497     {
 2498       self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 2499       g_string_sprintf(self->error_info, "Too long hostname in URL: %s", self->request_url_parts.host->str);
 2500       /*LOG
 2501         This message indicates that the HTTP request was rejected because
 2502         the hostname part in the URL was too long. You can increase the permitted
 2503         limit by changing the max_hostname_length attribute.
 2504       */
 2505       z_proxy_log(self, HTTP_POLICY, 2, "Too long hostname in URL; host='%s', length='%" G_GSIZE_FORMAT "', max_hostname_length='%d'",
 2506                   self->request_url_parts.host->str, self->request_url_parts.host->len, self->max_hostname_length);
 2507       z_proxy_return(self, FALSE);
 2508     }
 2509 
 2510   if (self->remote_port == 0)
 2511     self->remote_port = http_get_default_port_for_protocol(self);
 2512 
 2513   if (!(self->request_flags & HTTP_REQ_FLG_CONNECT))
 2514     http_rewrite_host_header(self, self->request_url_parts.host->str, self->request_url_parts.host->len, self->remote_port, self->server_protocol);
 2515 
 2516   if (http_parent_proxy_enabled(self))
 2517     {
 2518       self->remote_port = self->parent_proxy_port;
 2519       g_string_assign(self->remote_server, self->parent_proxy->str);
 2520     }
 2521   else
 2522     {
 2523       g_string_assign(self->remote_server, self->request_url_parts.host->str);
 2524     }
 2525 
 2526   /*LOG
 2527     This is an accounting message that reports the requested method and URL.
 2528   */
 2529   z_proxy_log(self, HTTP_ACCOUNTING, 4, "Accounting; command='%s', url='%s'", self->request_method->str, self->request_url->str);
 2530   z_proxy_return(self, TRUE);
 2531 }
 2532 
 2533 /* FIXME: this code should be converted into an explicit referral to the
 2534  * modified headers, e.g. instead of looping over all the headers, lookup
 2535  * the necessary headers using http_lookup_header and modify the returned
 2536  * values */
 2537 static guint
 2538 http_request_filter_headers(HttpProxy *self, GString *name, GString *value)
 2539 {
 2540   gint res = HTTP_HDR_ACCEPT;
 2541 
 2542   z_proxy_enter(self);
 2543 
 2544   switch (self->request_type)
 2545     {
 2546     case HTTP_REQTYPE_SERVER:
 2547 
 2548       /* if we have a parent proxy, Connection -> Proxy-Connection
 2549        * otherwise leave it as is
 2550        */
 2551       if (strcasecmp(name->str, "Connection") == 0)
 2552         {
 2553           if (http_parent_proxy_enabled(self))
 2554             g_string_assign(name, "Proxy-Connection");
 2555 
 2556           http_assign_connection_hdr_value(self, value);
 2557         }
 2558       else if (strcasecmp(name->str, "Authorization") == 0)
 2559         {
 2560           if (self->auth)
 2561             {
 2562               /* if inband authentication was performed, drop the
 2563                * authentication header unless forwarding was explicitly
 2564                * requested */
 2565               if (self->auth_forward)
 2566                 {
 2567                   g_string_assign(value, self->auth_header_value->str);
 2568 
 2569                   /* if the upstream is a proxy, forward it as a
 2570                    * Proxy-Authorization header */
 2571 
 2572                   if (http_parent_proxy_enabled(self))
 2573                     g_string_assign(name, "Proxy-Authorization");
 2574                 }
 2575               else
 2576                 {
 2577                   res = HTTP_HDR_DROP;
 2578                 }
 2579             }
 2580         }
 2581 
 2582       break;
 2583 
 2584     case HTTP_REQTYPE_PROXY:
 2585 
 2586       /* if we have a parent proxy leave it as is
 2587        * otherwise Proxy-Connection -> Connection
 2588        */
 2589       if (strcasecmp(name->str, "Proxy-Connection") == 0)
 2590         {
 2591           if (http_parent_proxy_enabled(self) == 0)
 2592             g_string_assign(name, "Connection");
 2593 
 2594           http_assign_connection_hdr_value(self, value);
 2595         }
 2596       else if (strcasecmp(name->str, "Proxy-Authorization") == 0)
 2597         {
 2598           if (self->auth)
 2599             {
 2600               /* if inband authentication was performed, drop the
 2601                * authentication header unless forwarding was explicitly
 2602                * requested */
 2603               if (self->auth_forward)
 2604                 {
 2605                   g_string_assign(value, self->auth_header_value->str);
 2606 
 2607                   /* if the upstream is not a proxy, forward it as a
 2608                    * Authorization header */
 2609                   if (!http_parent_proxy_enabled(self))
 2610                     g_string_assign(name, "Authorization");
 2611                 }
 2612               else
 2613                 {
 2614                   res = HTTP_HDR_DROP;
 2615                 }
 2616             }
 2617         }
 2618 
 2619       break;
 2620     }
 2621 
 2622   z_proxy_return(self, res);
 2623 }
 2624 
 2625 static gboolean
 2626 http_filter_request(HttpProxy *self)
 2627 {
 2628   ZPolicyObj *f;
 2629   gint rc;
 2630 
 2631   z_proxy_enter(self);
 2632   f = static_cast<ZPolicyObj *>(g_hash_table_lookup(self->request_method_policy, self->request_method->str));
 2633 
 2634   if (!f)
 2635     f = static_cast<ZPolicyObj *>(g_hash_table_lookup(self->request_method_policy, "*"));
 2636 
 2637   if (f)
 2638     {
 2639       ZPolicyObj *handler, *res;
 2640       gchar *errmsg;
 2641       guint filter_type;
 2642 
 2643       z_policy_lock(self->super.thread);
 2644 
 2645       if (!z_policy_tuple_get_verdict(f, &filter_type))
 2646         {
 2647           /*LOG
 2648             This message indicates that the request hash contains an invalid
 2649             item for the given request method. Check your Zorp
 2650             configuration.
 2651           */
 2652           z_proxy_log(self, HTTP_POLICY, 1, "Invalid item in request hash; method='%s'", self->request_method->str);
 2653           z_proxy_report_invalid_policy(&(self->super));
 2654           z_policy_unlock(self->super.thread);
 2655           z_proxy_return(self, FALSE);
 2656 
 2657         }
 2658 
 2659       z_policy_unlock(self->super.thread);
 2660       g_string_sprintf(self->error_info, "Method %s denied by policy", self->request_method->str);
 2661 
 2662       switch (filter_type)
 2663         {
 2664         case HTTP_REQ_POLICY:
 2665           z_policy_lock(self->super.thread);
 2666 
 2667           if (!z_policy_var_parse(f, "(iO)", &filter_type, &handler))
 2668             {
 2669               /*LOG
 2670                 This message indicates that the request hash contains an
 2671                 invalid POLICY tuple for the given request method.  It
 2672                 should contain a valid call-back function in the tuple.
 2673               */
 2674               z_proxy_log(self, HTTP_POLICY, 1, "Error parsing HTTP_REQ_POLICY tuple in request hash; method='%s'", self->request_method->str);
 2675               z_proxy_report_invalid_policy(&(self->super));
 2676               z_policy_unlock(self->super.thread);
 2677               z_proxy_return(self, FALSE);
 2678             }
 2679 
 2680           res = z_policy_call_object(handler,
 2681                                      z_policy_var_build("(sss)",
 2682                                                         self->request_method->str, self->request_url->str, self->request_version),
 2683                                      self->super.session_id);
 2684 
 2685           if (!res || !z_policy_var_parse(res, "i", &rc))
 2686             {
 2687               rc = HTTP_REQ_REJECT;
 2688               g_string_assign(self->error_info, "Error in policy handler, or returned value not integer;");
 2689               z_proxy_report_policy_abort(&(self->super));
 2690             }
 2691 
 2692           z_policy_var_unref(res);
 2693           z_policy_unlock(self->super.thread);
 2694           break;
 2695 
 2696         case HTTP_REQ_REJECT:
 2697           errmsg = NULL;
 2698           z_policy_lock(self->super.thread);
 2699 
 2700           if (!z_policy_var_parse_tuple(f, "i|s", &filter_type, &errmsg))
 2701             {
 2702               /*LOG
 2703                 This message indicates that the request hash contains an
 2704                 invalid REJECT tuple for the given request method.  It
 2705                 should contain an error message, which is sent back to the
 2706                 client.
 2707               */
 2708               z_proxy_log(self, HTTP_POLICY, 1, "Error parsing HTTP_REQ_REJECT in request hash; req='%s'", self->request_method->str);
 2709               z_proxy_report_invalid_policy(&(self->super));
 2710               z_policy_unlock(self->super.thread);
 2711               z_proxy_return(self, FALSE);
 2712             }
 2713 
 2714           z_policy_unlock(self->super.thread);
 2715 
 2716           if (errmsg)
 2717             g_string_assign(self->error_info, errmsg);
 2718 
 2719           /* fallthrough */
 2720 
 2721         case HTTP_REQ_ACCEPT:
 2722         case HTTP_REQ_DENY:
 2723         case HTTP_REQ_ABORT:
 2724           /* dropped command */
 2725           rc = filter_type;
 2726           break;
 2727 
 2728         default:
 2729           /*LOG
 2730             This message indicates that the request hash contains an invalid
 2731             action for the given request method.  Check your Zorp
 2732             configuration.
 2733           */
 2734           z_proxy_log(self, HTTP_POLICY, 1, "Unknown request hash item; req='%s'", self->request_method->str);
 2735           z_proxy_return(self, FALSE);
 2736         }
 2737 
 2738       switch (rc)
 2739         {
 2740         case HTTP_REQ_ACCEPT:
 2741           g_string_truncate(self->error_info, 0);
 2742           break;
 2743 
 2744         case HTTP_REQ_CUSTOM_RESPONSE:
 2745           /*LOG
 2746             This log message indicates that the policy requested a custom response to be sent to the client.
 2747           */
 2748           z_proxy_log(self, HTTP_POLICY, 6, "Policy requested to send custom response; req='%s'", self->request_method->str);
 2749           self->send_custom_response = TRUE;
 2750           z_proxy_return(self, TRUE);
 2751 
 2752         default:
 2753           /*LOG
 2754             This log message indicates that the specified HTTP request was not permitted by your policy.
 2755           */
 2756           z_proxy_log(self, HTTP_POLICY, 2, "Request not permitted by policy; req='%s'", self->request_method->str);
 2757           if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 2758             self->error_code = HTTP_MSG_POLICY_VIOLATION;
 2759           z_proxy_return(self, FALSE);
 2760         }
 2761 
 2762       if (self->proto_version[EP_CLIENT] >= 0x0100)
 2763         {
 2764           if (!http_filter_headers(self, EP_CLIENT, http_request_filter_headers))
 2765             z_proxy_return(self, FALSE);
 2766         }
 2767 
 2768       z_proxy_return(self, TRUE);
 2769     }
 2770 
 2771   self->error_code = HTTP_MSG_POLICY_VIOLATION;
 2772   g_string_sprintf(self->error_info, "Method %s denied by policy", self->request_method->str);
 2773   /*LOG
 2774     This log message indicates that the specified HTTP request was not permitted by your policy.
 2775   */
 2776   z_proxy_log(self, HTTP_POLICY, 2, "Request not permitted by policy; req='%s'", self->request_method->str);
 2777   z_proxy_return(self, FALSE);
 2778 }
 2779 
 2780 static gboolean
 2781 http_server_stream_ready(HttpProxy *self)
 2782 {
 2783   gboolean res = FALSE;
 2784 
 2785   z_proxy_enter(self);
 2786 
 2787   if (self->super.endpoints[EP_SERVER])
 2788     {
 2789       GIOStatus rc;
 2790       gchar buf[1];
 2791       gsize bytes_read;
 2792       ZStream *stream = self->super.endpoints[EP_SERVER];
 2793 
 2794       z_stream_set_nonblock(stream, TRUE);
 2795       rc = z_stream_read(stream, &buf, sizeof(buf), &bytes_read, NULL);
 2796       z_stream_set_nonblock(stream, FALSE);
 2797 
 2798       if (rc == G_IO_STATUS_NORMAL || rc == G_IO_STATUS_AGAIN)
 2799         {
 2800           res = TRUE;
 2801 
 2802           if (bytes_read > 0 && !z_stream_unget(stream, buf, bytes_read, NULL))
 2803             {
 2804               z_proxy_log(self, HTTP_ERROR, 2, "Error while checking if server stream is ready, buffer full");
 2805               res = FALSE;
 2806             }
 2807         }
 2808     }
 2809 
 2810   z_proxy_return(self, res);
 2811 }
 2812 
 2813 gboolean
 2814 http_connect_server(HttpProxy *self)
 2815 {
 2816   z_proxy_enter(self);
 2817 
 2818   if (!self->super.endpoints[EP_SERVER] ||
 2819       !http_server_stream_ready(self) ||
 2820       (!self->transparent_mode &&
 2821        (strcasecmp(self->remote_server->str, self->connected_server->str) != 0 ||
 2822         self->remote_port != self->connected_port)) ||
 2823       self->force_reconnect)
 2824     {
 2825       gboolean success = FALSE;
 2826 
 2827       self->force_reconnect = FALSE;
 2828 
 2829       if (self->super.endpoints[EP_SERVER])
 2830         {
 2831           z_stream_shutdown(self->super.endpoints[EP_SERVER], SHUT_RDWR, NULL);
 2832           z_stream_close(self->super.endpoints[EP_SERVER], NULL);
 2833           z_stream_unref(self->super.endpoints[EP_SERVER]);
 2834           self->super.endpoints[EP_SERVER] = NULL;
 2835 
 2836           z_proxy_ssl_clear_session(&self->super, EP_SERVER);
 2837         }
 2838 
 2839       g_string_sprintf(self->error_info, "Error establishing connection to %s", self->remote_server->str);
 2840 
 2841       if (http_parent_proxy_enabled(self))
 2842         {
 2843           success = z_proxy_connect_server(&self->super, self->parent_proxy->str, self->parent_proxy_port);
 2844         }
 2845       else if (self->transparent_mode && self->use_default_port_in_transparent_mode)
 2846         {
 2847           success = z_proxy_connect_server(&self->super, self->remote_server->str, http_get_default_port_for_protocol(self));
 2848         }
 2849       else if (z_port_enabled(self->target_port_range->str, self->remote_port))
 2850         {
 2851           success = z_proxy_connect_server(&self->super, self->remote_server->str, self->remote_port);
 2852         }
 2853       else
 2854         {
 2855           /*LOG
 2856             This message indicates that the proxy did not allow
 2857             addressing the specified port as the target_port_range
 2858             attribute does not allow it.
 2859           */
 2860           z_proxy_log(self, HTTP_VIOLATION, 2, "Connecting to this port is prohibited by policy; host='%s', port='%d'", self->remote_server->str, self->remote_port);
 2861           g_string_sprintf(self->error_info, "Connecting to port %d is prohibited by policy.", self->remote_port);
 2862           success = FALSE;
 2863         }
 2864 
 2865       if (!success)
 2866         {
 2867           /* error connecting to server */
 2868           self->error_code = HTTP_MSG_CONNECT_ERROR;
 2869           self->error_status = 502;
 2870           /* connect_server already logged */
 2871           z_proxy_return(self, FALSE);
 2872         }
 2873 
 2874       g_string_assign(self->connected_server, self->remote_server->str);
 2875       self->connected_port = self->remote_port;
 2876     }
 2877 
 2878   if (!http_server_stream_is_initialized(self)
 2879       && !http_server_stream_init(self))
 2880     {
 2881       /* should never happen */
 2882       /*LOG
 2883         This message indicates that initializing the server stream
 2884         failed. Please report this event to the BalaSys Development Team (at
 2885         devel@balasys.hu).
 2886       */
 2887       z_proxy_log(self, HTTP_ERROR, 1, "Internal error initializing server stream;");
 2888       z_proxy_return(self, FALSE);
 2889     }
 2890 
 2891   z_proxy_return(self, TRUE);
 2892 }
 2893 
 2894 static gboolean
 2895 http_copy_request(HttpProxy *self)
 2896 {
 2897   ZStream *blob_stream = NULL;
 2898 
 2899   z_proxy_enter(self);
 2900 
 2901   if (!http_connect_server(self))
 2902     z_proxy_return(self, FALSE); /* connect_server already logs */
 2903 
 2904   if (!http_check_name(self))
 2905     {
 2906       z_proxy_return(self, FALSE);
 2907     }
 2908 
 2909   if (self->request_data_stored && self->request_data && self->request_data->size > 0)
 2910     {
 2911       gchar session_id[MAX_SESSION_ID];
 2912 
 2913       g_snprintf(session_id, sizeof(session_id), "%s/post", self->super.session_id);
 2914       blob_stream = z_stream_blob_new(self->request_data, session_id);
 2915       blob_stream->timeout = -1;
 2916     }
 2917 
 2918   if (!http_data_transfer(self, blob_stream ? HTTP_TRANSFER_FROM_BLOB : HTTP_TRANSFER_NORMAL, EP_CLIENT, blob_stream ? blob_stream : self->super.endpoints[EP_CLIENT], EP_SERVER, self->super.endpoints[EP_SERVER], FALSE, FALSE, http_format_request))
 2919     {
 2920       /* http_data_transfer already logs */
 2921       z_stream_unref(blob_stream);
 2922       z_proxy_return(self, FALSE);
 2923     }
 2924 
 2925   z_stream_unref(blob_stream);
 2926   z_proxy_return(self, TRUE);
 2927 }
 2928 
 2929 static gboolean
 2930 http_fetch_response(HttpProxy *self)
 2931 {
 2932   gchar *line;
 2933   gsize line_length, br;
 2934   GIOStatus res;
 2935   gchar status[4];
 2936 
 2937   z_proxy_enter(self);
 2938   /* FIXME: this can probably be removed as http_fetch_header does this */
 2939   http_clear_headers(&self->headers[EP_SERVER]);
 2940   self->response[0] = 0;
 2941   self->response_code = -1;
 2942 
 2943   if (self->proto_version[EP_CLIENT] < 0x0100)
 2944     {
 2945       self->proto_version[EP_SERVER] = 0x0009;
 2946       z_proxy_return(self, TRUE);
 2947     }
 2948 
 2949   while (self->response_code == -1 || self->response_code == 100 || self->response_code == 102)
 2950     {
 2951       self->super.endpoints[EP_SERVER]->timeout = self->timeout_response;
 2952       res = z_stream_read_chunk(self->super.endpoints[EP_SERVER], status, sizeof(status), &br, NULL);
 2953       self->super.endpoints[EP_SERVER]->timeout = self->timeout;
 2954 
 2955       if (res != G_IO_STATUS_NORMAL)
 2956         {
 2957           /* the server closed our connection */
 2958           self->error_code = HTTP_MSG_OK;
 2959           z_proxy_return(self, FALSE);
 2960         }
 2961 
 2962       if (!z_stream_unget(self->super.endpoints[EP_SERVER], status, br, NULL))
 2963         {
 2964           /* error falling back to 0.9 */
 2965           /*LOG
 2966             This message indicates that Zorp was unable to enable HTTP/0.9
 2967             compatibility mode, due to the full buffer.  If you experience
 2968             this problem many times, please contact your Zorp support.
 2969           */
 2970           z_proxy_log(self, HTTP_ERROR, 2, "Error in HTTP/0.9 compatibility code, line buffer full;");
 2971           z_proxy_return(self, FALSE);
 2972         }
 2973 
 2974       if (br == 4 && memcmp(status, "HTTP", 4) == 0)
 2975         {
 2976           res = z_stream_line_get(self->super.endpoints[EP_SERVER], &line, &line_length, NULL);
 2977 
 2978           if (res != G_IO_STATUS_NORMAL)
 2979             {
 2980               self->error_code = HTTP_MSG_OK;
 2981               z_proxy_return(self, FALSE);
 2982             }
 2983         }
 2984       else if (self->permit_http09_responses)
 2985         {
 2986           self->proto_version[EP_SERVER] = 0x0009;
 2987           z_proxy_return(self, TRUE);
 2988         }
 2989       else
 2990         {
 2991           /* HTTP/0.9 is not permitted by policy */
 2992           g_string_assign(self->error_info, "Server falled back to HTTP/0.9 which is prohibited by policy.");
 2993           /*LOG
 2994             This message indicates that the server sent back HTTP/0.9
 2995             response, which is prohibited by the policy.  It is likely a
 2996             buggy or old server. Check the permit_http09_responses
 2997             attribute.
 2998           */
 2999           z_proxy_log(self, HTTP_POLICY, 2, "Server falled back to HTTP/0.9 which is prohibited by policy;");
 3000           z_proxy_return(self, FALSE);
 3001         }
 3002 
 3003       if (!http_split_response(self, line, line_length))
 3004         {
 3005           /*LOG
 3006             This message indicates the the HTTP status line returned by the
 3007             server was invalid.
 3008           */
 3009           z_proxy_log(self, HTTP_VIOLATION, 1, "Invalid HTTP response heading; line='%.*s'", (gint)line_length, line);
 3010           g_string_assign(self->error_info, "Invalid HTTP response heading");
 3011           z_proxy_return(self, FALSE);
 3012         }
 3013 
 3014       self->response_flags = http_proto_response_lookup(self->response);
 3015 
 3016       if (!http_parse_version(self, EP_SERVER, self->response_version))
 3017         {
 3018           g_string_sprintf(self->error_info, "Invalid HTTP version in response (%.*s)", (gint) line_length, line);
 3019           /*LOG
 3020             This message indicates that the server sent the response with an
 3021             invalid HTTP version.  It is likely that the server is buggy.
 3022           */
 3023           z_proxy_log(self, HTTP_VIOLATION, 1, "Error parsing response version; line='%.*s'", (gint) line_length, line);
 3024           z_proxy_return(self, FALSE);
 3025         }
 3026 
 3027       if (!http_fetch_headers(self, EP_SERVER))
 3028         {
 3029           g_string_assign(self->error_info, "Invalid headers received in response");
 3030           z_proxy_return(self, FALSE);
 3031         }
 3032 
 3033       if (self->append_cookie)
 3034         {
 3035           http_add_header(&self->headers[EP_SERVER], "Set-Cookie", strlen("Set-Cookie"),
 3036                           self->append_cookie->str, self->append_cookie->len);
 3037           g_string_free(self->append_cookie, TRUE);
 3038           self->append_cookie = NULL;
 3039         }
 3040     }
 3041 
 3042   z_proxy_return(self, TRUE);
 3043 }
 3044 
 3045 static gboolean
 3046 http_process_response(HttpProxy *self)
 3047 {
 3048   HttpHeader *pconn_hdr = NULL, *conn_hdr = NULL;
 3049 
 3050   z_proxy_enter(self);
 3051 
 3052   /* previously set by http_parse_version */
 3053   switch (self->proto_version[EP_SERVER])
 3054     {
 3055     case 0x0009:
 3056       g_strlcpy(self->response, "200", sizeof(self->response_code));
 3057       self->response_code = 200;
 3058       self->response_flags = 0;
 3059       self->connection_mode = HTTP_CONNECTION_CLOSE;
 3060       self->server_connection_mode = HTTP_CONNECTION_CLOSE;
 3061       z_proxy_return(self, TRUE);
 3062 
 3063     case 0x0100:
 3064       self->server_connection_mode = HTTP_CONNECTION_CLOSE;
 3065       break;
 3066 
 3067     case 0x0101:
 3068       self->server_connection_mode = HTTP_CONNECTION_KEEPALIVE;
 3069       break;
 3070 
 3071     default:
 3072       /*LOG
 3073         This message indicates that the server sent an unsupported protocol
 3074         version. It is likely that the server is buggy.
 3075       */
 3076       z_proxy_log(self, HTTP_VIOLATION, 1, "Unsupported protocol version; version='0x%04x'", self->proto_version[EP_SERVER]);
 3077       z_proxy_return(self, FALSE);
 3078     }
 3079 
 3080   /* process connection header */
 3081   self->connection_hdr = NULL;
 3082   http_lookup_header(&self->headers[EP_SERVER], "Proxy-Connection", &pconn_hdr);
 3083   http_lookup_header(&self->headers[EP_SERVER], "Connection", &conn_hdr);
 3084 
 3085   if ((http_parent_proxy_enabled(self) && pconn_hdr) || conn_hdr)
 3086     {
 3087       /* override default */
 3088       if (http_parent_proxy_enabled(self) && pconn_hdr)
 3089         {
 3090           self->connection_hdr = pconn_hdr;
 3091 
 3092           if (conn_hdr)
 3093             conn_hdr->present = FALSE;
 3094         }
 3095       else
 3096         {
 3097           self->connection_hdr = conn_hdr;
 3098 
 3099           if (pconn_hdr)
 3100             pconn_hdr->present = FALSE;
 3101         }
 3102 
 3103       if (self->connection_mode == HTTP_CONNECTION_KEEPALIVE &&
 3104           http_parse_connection_hdr_value(self, self->connection_hdr) != HTTP_CONNECTION_CLOSE)
 3105         self->server_connection_mode = HTTP_CONNECTION_KEEPALIVE;
 3106       else
 3107         self->server_connection_mode = HTTP_CONNECTION_CLOSE;
 3108     }
 3109   else
 3110     {
 3111       /* NOTE: there was no appropriate Connection header in the response, if it
 3112        * is an 1.0 client the connection mode should be changed to
 3113        * connection close regardless what the client specified in its
 3114        * connection header.
 3115        */
 3116       const gchar *conn_hdr_str = http_parent_proxy_enabled(self) ? "Proxy-Connection" : "Connection";
 3117 
 3118       /* we add an appriopriate connection header just as if we received one
 3119        * (e.g. it might be rewritten later by
 3120        * http_response_filter_connection_header), we default to not sending
 3121        * this new header, it will be made visible when we want to ensure
 3122        * that our connection mode must be enforced. */
 3123       self->connection_hdr = http_add_header(&self->headers[EP_SERVER], conn_hdr_str, strlen(conn_hdr_str), "close", 5);
 3124       self->connection_hdr->present = FALSE;
 3125 
 3126       if (self->proto_version[EP_CLIENT] == 0x0100)
 3127         {
 3128           if (self->connection_mode == HTTP_CONNECTION_KEEPALIVE)
 3129             {
 3130               /* it is somewhat uncertain what happens when an 1.0 client
 3131                * requests keepalive and an 1.1 server responds without a
 3132                * connection header, resolve this uncertainity by adding a
 3133                * connection header */
 3134               self->connection_hdr->present = TRUE;
 3135             }
 3136 
 3137           self->server_connection_mode = HTTP_CONNECTION_CLOSE;
 3138         }
 3139     }
 3140 
 3141   if (self->request_flags & HTTP_REQ_FLG_CONNECT && self->response_code == 200)
 3142     self->server_connection_mode = HTTP_CONNECTION_CLOSE;
 3143 
 3144   if (!self->keep_persistent && self->server_connection_mode == HTTP_CONNECTION_CLOSE)
 3145     self->connection_mode = HTTP_CONNECTION_CLOSE;
 3146 
 3147   if (self->response_flags & HTTP_RESP_FLG_STOP)
 3148     z_proxy_return(self, FALSE); /* FIXME: not implemented */
 3149 
 3150   z_proxy_return(self, TRUE);
 3151 }
 3152 
 3153 /**
 3154  * http_fetch_buffered_data:
 3155  * @self: HttpProxy instance
 3156  *
 3157  * Read all buffered data from the client up to maximum ten 4096 chunks.
 3158  * This is needed to avoid sending RSTs to connections where the client sent
 3159  * some unprocessed bytes (like IE 5.0 in the case of POST requests).
 3160  **/
 3161 static void
 3162 http_fetch_buffered_data(HttpProxy *self)
 3163 {
 3164   gchar buf[4096];
 3165   gsize br;
 3166   gint count = 0;
 3167 
 3168   z_stream_set_nonblock(self->super.endpoints[EP_CLIENT], TRUE);
 3169 
 3170   while (count < 10 && z_stream_read(self->super.endpoints[EP_CLIENT], buf, sizeof(buf), &br, NULL) == G_IO_STATUS_NORMAL)
 3171     {
 3172       count++;
 3173     }
 3174 
 3175   z_stream_set_nonblock(self->super.endpoints[EP_CLIENT], FALSE);
 3176 }
 3177 
 3178 static ZPolicyObj *
 3179 http_response_policy_get(HttpProxy *self)
 3180 {
 3181   gchar *response_keys[2];
 3182 
 3183   response_keys[0] = self->request_method->str;
 3184   response_keys[1] = self->response;
 3185 
 3186   return static_cast<ZPolicyObj *>(z_dim_hash_table_search(self->response_policy, 2, response_keys));
 3187 }
 3188 
 3189 static gboolean
 3190 http_filter_response(HttpProxy *self)
 3191 {
 3192   z_proxy_enter(self);
 3193 
 3194   if (self->proto_version[EP_SERVER] >= 0x0100)
 3195     {
 3196       ZPolicyObj *f, *res;
 3197       gint rc;
 3198 
 3199       f = http_response_policy_get(self);
 3200 
 3201       if (f)
 3202         {
 3203           ZPolicyObj *handler;
 3204           guint filter_type;
 3205           gchar *errmsg;
 3206 
 3207           z_policy_lock(self->super.thread);
 3208 
 3209           if (!z_policy_tuple_get_verdict(f, &filter_type))
 3210             {
 3211               /*LOG
 3212                 This message indicates that the response hash contains an
 3213                 invalid item for the given response. Check your Zorp
 3214                 configuration.
 3215               */
 3216               z_proxy_log(self, HTTP_POLICY, 1, "Invalid response hash item; request='%s', response='%d'", self->request_method->str, self->response_code);
 3217               z_proxy_report_invalid_policy(&(self->super));
 3218               z_policy_unlock(self->super.thread);
 3219               z_proxy_return(self, FALSE);
 3220             }
 3221 
 3222           z_policy_unlock(self->super.thread);
 3223 
 3224           g_string_sprintf(self->error_info, "Response %d for %s denied by policy.", self->response_code, self->request_method->str);
 3225 
 3226           switch (filter_type)
 3227             {
 3228             case HTTP_RSP_POLICY:
 3229               z_policy_lock(self->super.thread);
 3230 
 3231               if (!z_policy_var_parse(f, "(iO)", &filter_type, &handler))
 3232                 {
 3233                   /*LOG
 3234                     This message indicates that the response hash contains
 3235                     an invalid POLICY tuple for the given response.  It
 3236                     should contain a valid call-back function in the tuple.
 3237                   */
 3238                   z_proxy_log(self, HTTP_POLICY, 1,
 3239                               "Error parsing HTTP_RSP_POLICY in response hash; request='%s', response='%d'",
 3240                               self->request_method->str, self->response_code);
 3241                   z_proxy_report_invalid_policy(&(self->super));
 3242                   z_policy_unlock(self->super.thread);
 3243                   z_proxy_return(self, FALSE);
 3244                 }
 3245 
 3246               res = z_policy_call_object(handler, z_policy_var_build("(sssi)", self->request_method->str, self->request_url->str, self->request_version, self->response_code), self->super.session_id);
 3247 
 3248               if (!z_policy_var_parse(res, "i", &rc))
 3249                 {
 3250                   g_string_assign(self->error_info, "Error in policy handler.");
 3251                   z_proxy_report_policy_abort(&(self->super));
 3252                   rc = HTTP_RSP_REJECT;
 3253                 }
 3254 
 3255               z_policy_var_unref(res);
 3256               z_policy_unlock(self->super.thread);
 3257               break;
 3258 
 3259             case HTTP_RSP_REJECT:
 3260               errmsg = NULL;
 3261               z_policy_lock(self->super.thread);
 3262 
 3263               if (!z_policy_var_parse_tuple(f, "i|s", &filter_type, &errmsg))
 3264                 {
 3265                   /*LOG
 3266                     This message indicates that the response hash contains
 3267                     an invalid REJECT tuple for the given response.
 3268                     It should contain an error message, which is sent back to
 3269                     the client.
 3270                   */
 3271                   z_proxy_log(self, HTTP_POLICY, 1,
 3272                               "Error parsing HTTP_RSP_REJECT in response hash; request='%s', response='%d'",
 3273                               self->request_method->str, self->response_code);
 3274                   z_policy_unlock(self->super.thread);
 3275                   z_proxy_return(self, FALSE);
 3276                 }
 3277 
 3278               z_policy_unlock(self->super.thread);
 3279 
 3280               if (errmsg)
 3281                 g_string_assign(self->error_info, errmsg);
 3282 
 3283               /* fallthrough */
 3284 
 3285             case HTTP_RSP_ACCEPT:
 3286             case HTTP_RSP_DENY:
 3287             case HTTP_RSP_ABORT:
 3288               /* dropped command */
 3289               rc = filter_type;
 3290               break;
 3291 
 3292             default:
 3293               /*LOG
 3294                 This message indicates that the response hash contains
 3295                 an invalid action for the given response.
 3296                 Check your Zorp configuration.
 3297               */
 3298               z_proxy_log(self, HTTP_POLICY, 1,
 3299                           "Invalid response hash item; request='%s', response='%d'",
 3300                           self->request_method->str, self->response_code);
 3301               z_proxy_return(self, FALSE);
 3302             }
 3303 
 3304           switch (rc)
 3305             {
 3306             case HTTP_RSP_ACCEPT:
 3307               break;
 3308 
 3309             default:
 3310             case HTTP_RSP_REJECT:
 3311               /*LOG
 3312                 This message indicates that the status code returned by the server is
 3313                 not a permitted response code for this request.
 3314               */
 3315               z_proxy_log(self, HTTP_POLICY, 2, "Response not permitted by policy; req='%s', rsp='%d'", self->request_method->str, self->response_code);
 3316               self->error_code = HTTP_MSG_POLICY_VIOLATION;
 3317               z_proxy_return(self, FALSE);
 3318             }
 3319 
 3320         }
 3321 
 3322       if (!http_filter_headers(self, EP_SERVER, NULL))
 3323         z_proxy_return(self, FALSE);
 3324     }
 3325 
 3326   z_proxy_return(self, TRUE);
 3327 }
 3328 
 3329 static gboolean
 3330 http_format_response(HttpProxy *self, gboolean stacked, GString *response)
 3331 {
 3332   if (self->proto_version[EP_SERVER] >= 0x0100)
 3333     {
 3334       if (!stacked)
 3335         {
 3336           http_flat_headers(&self->headers[EP_SERVER]);
 3337           g_string_set_size(response, 4000);
 3338           g_string_truncate(response, 0);
 3339 
 3340           g_string_append_const(response, "HTTP");
 3341           g_string_append_c(response, '/');
 3342           g_string_append_c(response, '0' + ((self->proto_version[EP_SERVER] & 0xFF00) >> 8));
 3343           g_string_append_c(response, '.');
 3344           g_string_append_c(response, '0' + (self->proto_version[EP_SERVER] & 0x00FF));
 3345           g_string_append_c(response, ' ');
 3346           g_string_append(response, self->response);
 3347           g_string_append_c(response, ' ');
 3348           g_string_append_gstring(response, self->response_msg);
 3349           g_string_append_const(response, "\r\n");
 3350           g_string_append_gstring(response, self->headers[EP_SERVER].flat);
 3351           g_string_append_const(response, "\r\n");
 3352         }
 3353       else
 3354         {
 3355           http_flat_headers_into(&self->headers[EP_SERVER], response);
 3356         }
 3357     }
 3358   else
 3359     g_string_truncate(response, 0);
 3360 
 3361   return TRUE;
 3362 }
 3363 
 3364 static gboolean
 3365 http_copy_response(HttpProxy *self)
 3366 {
 3367   gboolean suppress_data, expect_data;
 3368 
 3369   z_proxy_enter(self);
 3370 
 3371   /* self->connection_hdr must never be NULL for server->client direction when at least HTTP/1.0 is used */
 3372   if (self->proto_version[EP_SERVER] >= 0x0100)
 3373     {
 3374       g_assert(self->connection_hdr);
 3375 
 3376       if (self->connection_mode != self->server_connection_mode)
 3377         self->connection_hdr->present = TRUE;
 3378 
 3379       if (self->connection_hdr->present)
 3380         {
 3381           g_string_assign(self->connection_hdr->name, self->request_type == HTTP_REQTYPE_SERVER ? "Connection" : "Proxy-Connection");
 3382           http_assign_connection_hdr_value(self, self->connection_hdr->value);
 3383         }
 3384     }
 3385 
 3386   expect_data = (self->response_flags & HTTP_RESP_FLG_DONTEXPECT) == 0;
 3387   suppress_data = (self->response_flags & HTTP_RESP_FLG_SUPPRESS) != 0 ||
 3388     (self->request_flags & HTTP_REQ_FLG_HEAD) != 0 ||
 3389     ((self->request_flags & HTTP_REQ_FLG_CONNECT) != 0 && self->response_code == 200);
 3390 
 3391   if (!http_data_transfer(self, HTTP_TRANSFER_NORMAL, EP_SERVER, self->super.endpoints[EP_SERVER], EP_CLIENT, self->super.endpoints[EP_CLIENT], expect_data, suppress_data, http_format_response))
 3392     z_proxy_return(self, FALSE); /* http_data_transfer already logs */
 3393 
 3394   z_proxy_return(self, TRUE);
 3395 }
 3396 
 3397 static gboolean
 3398 http_handle_connect(HttpProxy *self)
 3399 {
 3400   /* connect is enabled here */
 3401   guint i;
 3402   ZPolicyObj *res;
 3403   gboolean called;
 3404   gchar *success, *remote_host;
 3405   gchar *colon, *end;
 3406   gint remote_host_len, remote_port;
 3407 
 3408   z_proxy_enter(self);
 3409   self->connection_mode = HTTP_CONNECTION_CLOSE;
 3410   colon = strchr(self->request_url->str, ':');
 3411 
 3412   if (colon)
 3413     {
 3414       remote_host = self->request_url->str;
 3415       remote_host_len = colon - remote_host;
 3416       remote_port = strtoul(colon + 1, &end, 10);
 3417     }
 3418   else
 3419     {
 3420       self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 3421       g_string_sprintf(self->error_info, "Invalid CONNECT request.");
 3422       /*LOG
 3423         This message indicates that the received CONNECT request did not
 3424         include a port number to connect to.
 3425       */
 3426       z_proxy_log(self, HTTP_VIOLATION, 1, "Missing port number in CONNECT request; url='%s'", self->request_url->str);
 3427       z_proxy_return(self, FALSE);
 3428     }
 3429 
 3430   if (http_parent_proxy_enabled(self))
 3431     {
 3432       g_string_assign(self->remote_server, self->parent_proxy->str);
 3433       self->remote_port = self->parent_proxy_port;
 3434     }
 3435   else
 3436     {
 3437       /* From buffer is longer than we want to copy. */
 3438       g_string_assign_len(self->remote_server, remote_host, remote_host_len);
 3439       self->remote_port = remote_port;
 3440     }
 3441 
 3442   if (!http_connect_server(self))
 3443     z_proxy_return(self, FALSE);
 3444 
 3445   if (http_parent_proxy_enabled(self))
 3446     {
 3447       GString *request = g_string_sized_new(64);
 3448 
 3449       http_format_request(self, FALSE, request);
 3450 
 3451       if (http_write(self, EP_SERVER, request->str, request->len) != G_IO_STATUS_NORMAL)
 3452         {
 3453           g_string_free(request, TRUE);
 3454           z_proxy_return(self, FALSE);
 3455         }
 3456 
 3457       g_string_free(request, TRUE);
 3458 
 3459       if (!http_fetch_response(self))
 3460         z_proxy_return(self, FALSE);
 3461 
 3462       if (self->response_code != 200)
 3463         {
 3464           /*LOG
 3465             This message indicates that the our parent proxy
 3466             refused our CONNECT request. It is likely that the
 3467             parent proxy does not permit CONNECT method.
 3468           */
 3469           z_proxy_log(self, HTTP_ERROR, 1, "Parent proxy refused our CONNECT request; host='%.*s', port='%d'", remote_host_len, remote_host, remote_port);
 3470           z_proxy_log(self, HTTP_DEBUG, 6, "Processing response and headers (CONNECT);");
 3471 
 3472           if (!http_process_response(self) ||
 3473               !http_filter_response(self) ||
 3474               !http_copy_response(self))
 3475             {
 3476               z_proxy_log(self, HTTP_ERROR, 2, "Error copying CONNECT response, or CONNECT response rejected by policy; request='%s', response='%d'", self->request_method->str, self->response_code);
 3477             }
 3478 
 3479           z_proxy_return(self, FALSE);
 3480         }
 3481     }
 3482 
 3483   /*LOG
 3484     This message reports that CONNECT method is in use, and
 3485     CONNECT method was accepted by out parent proxy.
 3486   */
 3487   z_proxy_log(self, HTTP_DEBUG, 6, "Starting connect method;");
 3488 
 3489   if (!http_parent_proxy_enabled(self))
 3490     {
 3491       success = const_cast<char*>("HTTP/1.0 200 Connection established\r\n\r\n");
 3492 
 3493       if (http_write(self, EP_CLIENT, success, strlen(success)) != G_IO_STATUS_NORMAL)
 3494         z_proxy_return(self, FALSE); /* hmm... I/O error */
 3495     }
 3496   else
 3497     {
 3498       if (!http_process_response(self) ||
 3499           !http_filter_response(self) ||
 3500           !http_copy_response(self))
 3501         {
 3502           z_proxy_log(self, HTTP_ERROR, 2, "Error copying CONNECT response, or CONNECT response rejected by policy; request='%s', response='%d'", self->request_method->str, self->response_code);
 3503           z_proxy_return(self, FALSE);
 3504         }
 3505     }
 3506 
 3507   /* FIXME: flush already buffered data, before starting plug */
 3508 
 3509   /* NOTE: the child proxy uses the Python variables session.client_stream
 3510    * and session.server_stream as its streams (see bug: #10347)
 3511    */
 3512   for (i = EP_CLIENT; i < EP_MAX; i++)
 3513     {
 3514       while (self->super.endpoints[i]->child)
 3515         self->super.endpoints[i] = z_stream_pop(self->super.endpoints[i]);
 3516     }
 3517 
 3518   z_policy_lock(self->super.thread);
 3519   res = z_policy_call(self->super.handler, "connectMethod", z_policy_var_build("()"), &called, self->super.session_id);
 3520 
 3521   if (!res || res == z_policy_none)
 3522     {
 3523       /*LOG
 3524         This message indicates that an internal error occurred, the
 3525         connectMethod function did not return an integer. Please report this
 3526         event to the BalaSys Development Team (at devel@balasys.hu).
 3527       */
 3528       z_proxy_log(self, HTTP_ERROR, 1, "Internal error, connectMethod is expected to return a proxy instance;");
 3529       z_proxy_report_policy_abort(&(self->super));
 3530       self->error_code = HTTP_MSG_POLICY_SYNTAX;
 3531       z_policy_var_unref(res);
 3532       z_policy_unlock(self->super.thread);
 3533       z_proxy_leave(self);
 3534       return FALSE;
 3535     }
 3536 
 3537   z_policy_var_unref(res);
 3538   z_policy_unlock(self->super.thread);
 3539 
 3540   /* release, but don't close fds */
 3541   for (i = EP_CLIENT; i < EP_MAX; i++)
 3542     {
 3543       z_stream_unref(self->super.endpoints[i]);
 3544       self->super.endpoints[i] = NULL;
 3545     }
 3546 
 3547   z_proxy_return(self, TRUE);
 3548 }
 3549 
 3550 static void
 3551 http_main(ZProxy *s)
 3552 {
 3553   HttpProxy *self = Z_CAST(s, HttpProxy);
 3554   gint rerequest_attempts;
 3555 
 3556   z_proxy_enter(self);
 3557   self->request_count = 0;
 3558   http_client_stream_init(self);
 3559 
 3560   /* loop for keep-alive */
 3561   while (1)
 3562     {
 3563       self->error_code = HTTP_MSG_NOT_ASSIGNED;
 3564 
 3565       /*LOG
 3566         This message reports that Zorp is fetching the request and the
 3567         headers from the client.
 3568       */
 3569       z_proxy_log(self, HTTP_DEBUG, 6, "Fetching request and headers;");
 3570 
 3571       /* fetch request */
 3572       if (!http_fetch_request(self))
 3573         {
 3574           if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3575             self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 3576 
 3577           goto exit_request_loop;
 3578         }
 3579 
 3580       if (!z_proxy_loop_iteration(s))
 3581         break;
 3582 
 3583       /*LOG
 3584         This message reports that Zorp is processing the fetched request and
 3585         the headers.
 3586       */
 3587       z_proxy_log(self, HTTP_DEBUG, 6, "processing request and headers;");
 3588 
 3589       if (!http_process_request(self))
 3590         {
 3591           if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3592             self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 3593 
 3594           goto exit_request_loop;
 3595         }
 3596 
 3597       if (self->max_keepalive_requests != 0 &&
 3598           self->request_count >= self->max_keepalive_requests - 1)
 3599         {
 3600           /*LOG
 3601             This message reports that the maximum number of requests in a keep-alive loop is
 3602             reached, and Zorp is closing after this request. Check the max_keepalive_request
 3603             attribute.
 3604           */
 3605           z_proxy_log(self, HTTP_POLICY, 3,
 3606                       "Maximum keep-alive request reached, setting connection mode to close; count='%d', max='%d'",
 3607                       self->request_count, self->max_keepalive_requests);
 3608           self->connection_mode = HTTP_CONNECTION_CLOSE;
 3609         }
 3610 
 3611       /*LOG
 3612         This message reports that Zorp is filtering the processed
 3613         request and the headers.
 3614       */
 3615       z_proxy_log(self, HTTP_DEBUG, 6, "Filtering request and headers;");
 3616 
 3617       if (!http_filter_request(self))
 3618         {
 3619           if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3620             self->error_code = HTTP_MSG_POLICY_SYNTAX;
 3621 
 3622           goto exit_request_loop;
 3623         }
 3624 
 3625       if (self->send_custom_response)
 3626         goto exit_request_loop;
 3627 
 3628       /*LOG
 3629         This message indicates that Zorp is recechecking the HTTP request
 3630         after possible changes performed by the policy layer.
 3631       */
 3632       z_proxy_log(self, HTTP_DEBUG, 6, "Reprocessing filtered request;");
 3633 
 3634       if (!http_process_filtered_request(self))
 3635         {
 3636           if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3637             self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 3638 
 3639           goto exit_request_loop;
 3640         }
 3641 
 3642       if (self->server_protocol == HTTP_PROTO_HTTP || self->server_protocol == HTTP_PROTO_HTTPS)
 3643         {
 3644           gboolean retry;
 3645 
 3646           if ((self->request_flags & HTTP_REQ_FLG_CONNECT))
 3647             {
 3648               if (http_handle_connect(self))
 3649                 z_proxy_return(self); /* connect method was successful, we can now safely quit */
 3650 
 3651               goto exit_request_loop;
 3652             }
 3653 
 3654           rerequest_attempts = self->rerequest_attempts;
 3655 
 3656           while (1)
 3657             {
 3658               retry = FALSE;
 3659               /*LOG
 3660                 This message reports that Zorp is sending the filtered request and
 3661                 headers, and copies the requests data to the server.
 3662               */
 3663               z_proxy_log(self, HTTP_DEBUG, 6, "Sending request and headers, copying request data;");
 3664 
 3665               if (!retry && !http_copy_request(self))
 3666                 {
 3667                   if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3668                     self->error_code = HTTP_MSG_CLIENT_SYNTAX;
 3669 
 3670                   retry = TRUE;
 3671                 }
 3672 
 3673               /*LOG
 3674                 This message reports that Zorp is fetching the response and headers
 3675                 from the server.
 3676               */
 3677               z_proxy_log(self, HTTP_DEBUG, 6, "Fetching response and headers;");
 3678 
 3679               if (!retry && !http_fetch_response(self))
 3680                 {
 3681                   if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3682                     self->error_code = HTTP_MSG_SERVER_SYNTAX;
 3683 
 3684                   retry = TRUE;
 3685                 }
 3686 
 3687               if (retry && rerequest_attempts > 1)
 3688                 {
 3689                   z_proxy_log(self, HTTP_ERROR, 3, "Server request failed, retrying; attempts='%d'", rerequest_attempts);
 3690                   self->force_reconnect = TRUE;
 3691                   self->error_code = HTTP_MSG_NOT_ASSIGNED;
 3692                   rerequest_attempts--;
 3693                   continue;
 3694                 }
 3695               else if (retry)
 3696                 {
 3697                   goto exit_request_loop;
 3698                 }
 3699               else
 3700                 {
 3701                   break;
 3702                 }
 3703             }
 3704 
 3705           if (!z_proxy_loop_iteration(s))
 3706             goto exit_request_loop;
 3707 
 3708           /*LOG
 3709             This message reports that Zorp is processing the fetched response
 3710             and the headers.
 3711           */
 3712           z_proxy_log(self, HTTP_DEBUG, 6, "Processing response and headers;");
 3713 
 3714           if (!http_process_response(self))
 3715             {
 3716               if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3717                 self->error_code = HTTP_MSG_SERVER_SYNTAX;
 3718 
 3719               goto exit_request_loop;
 3720             }
 3721 
 3722           /*LOG
 3723             This message reports that Zorp is filtering the processed
 3724             response and the headers.
 3725           */
 3726           z_proxy_log(self, HTTP_DEBUG, 6, "Filtering response and headers;");
 3727 
 3728           if (!http_filter_response(self))
 3729             {
 3730               if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3731                 self->error_code = HTTP_MSG_POLICY_SYNTAX;
 3732 
 3733               goto exit_request_loop;
 3734             }
 3735 
 3736           /*LOG
 3737             This message reports that Zorp is sending the filtered
 3738             response and headers, and copies the response data to the client.
 3739           */
 3740           z_proxy_log(self, HTTP_DEBUG, 6, "Copying response and headers, copying response data;");
 3741 
 3742           if (!http_copy_response(self))
 3743             {
 3744               if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3745                 self->error_code = HTTP_MSG_SERVER_SYNTAX;
 3746 
 3747               goto exit_request_loop;
 3748             }
 3749         }
 3750       else if (self->server_protocol == HTTP_PROTO_FTP)
 3751         {
 3752           if (!http_handle_ftp_request(self))
 3753             {
 3754               if (self->error_code == HTTP_MSG_NOT_ASSIGNED)
 3755                 self->error_code = HTTP_MSG_FTP_ERROR;
 3756             }
 3757 
 3758           self->connection_mode = HTTP_CONNECTION_CLOSE;
 3759         }
 3760       else
 3761         {
 3762           /*LOG
 3763             This message indicates an internal error in HTTP proxy. Please
 3764             report this event to the BalaSys Development Team (at
 3765         devel@balasys.hu).
 3766           */
 3767           z_proxy_log(self, CORE_ERROR, 1, "Internal error, invalid server_protocol; server_protocol='%d'", self->server_protocol);
 3768         }
 3769 
 3770       if (self->connection_mode == HTTP_CONNECTION_CLOSE)
 3771         goto exit_request_loop;
 3772 
 3773       if (self->server_connection_mode == HTTP_CONNECTION_CLOSE)
 3774         {
 3775           /* close the server connection, but keep the client connection in-tact */
 3776           self->force_reconnect = TRUE;
 3777         }
 3778 
 3779       self->request_count++;
 3780 
 3781       /* NOTE:
 3782        * In keepalive mode we have to disable authentication after the first round.
 3783        */
 3784       if (self->auth && !(self->auth_by_form || self->auth_by_cookie))
 3785         {
 3786           z_policy_lock(self->super.thread);
 3787 
 3788           z_policy_var_unref(self->auth);
 3789           self->auth = NULL;
 3790 
 3791           z_policy_unlock(self->super.thread);
 3792         }
 3793     }
 3794 
 3795  exit_request_loop:
 3796   /*LOG
 3797     This message reports that Zorp is exiting the keep-alive loop and
 3798     closing the connection.
 3799   */
 3800   z_proxy_log(self, HTTP_DEBUG, 6, "exiting keep-alive loop;");
 3801 
 3802   if (self->error_code > 0)
 3803     {
 3804       http_error_message(self, self->error_status, self->error_code, self->error_info);
 3805     }
 3806   else if (self->send_custom_response)
 3807     {
 3808       self->send_custom_response = FALSE;
 3809       http_send_custom_response(self, self->error_status, self->error_msg, self->error_headers, self->custom_response_body);
 3810     }
 3811 
 3812   /* in some cases the client might still have some data already queued to us,
 3813    * fetch and ignore that data to avoid the RST sent in these cases
 3814    */
 3815   http_fetch_buffered_data(self);
 3816 
 3817   if (self->error_code <= 0 && self->reset_on_close)
 3818     {
 3819       int fd;
 3820 
 3821       fd = z_stream_get_fd(self->super.endpoints[EP_CLIENT]);
 3822 
 3823       if (fd >= 0)
 3824         {
 3825           struct linger l;
 3826 
 3827           l.l_onoff = 1;
 3828           l.l_linger = 0;
 3829           setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
 3830         }
 3831 
 3832       /* avoid z_stream_shutdown in the destroy path */
 3833       z_stream_close(self->super.endpoints[EP_CLIENT], NULL);
 3834       z_stream_unref(self->super.endpoints[EP_CLIENT]);
 3835       self->super.endpoints[EP_CLIENT] = NULL;
 3836     }
 3837 
 3838   z_proxy_return(self);
 3839 }
 3840 
 3841 static gboolean
 3842 http_config(ZProxy *s)
 3843 {
 3844   HttpProxy *self = Z_CAST(s, HttpProxy);
 3845 
 3846   http_config_set_defaults(self);
 3847 
 3848   http_register_vars(self);
 3849 
 3850   if (Z_SUPER(s, ZProxy)->config(s))
 3851     {
 3852       http_config_init(self);
 3853 
 3854       z_proxy_ssl_set_force_connect_at_handshake(s, TRUE);
 3855 
 3856       return TRUE;
 3857     }
 3858 
 3859   return FALSE;
 3860 }
 3861 
 3862 static void
 3863 http_proxy_free(ZObject *s)
 3864 {
 3865   HttpProxy *self = Z_CAST(s, HttpProxy);
 3866   guint i;
 3867 
 3868   z_enter();
 3869 
 3870   for (i = EP_CLIENT; i < EP_MAX; i++)
 3871     http_destroy_headers(&self->headers[i]);
 3872 
 3873   if (self->request_data)
 3874     z_blob_unref(self->request_data);
 3875 
 3876   g_string_free(self->old_auth_header, TRUE);
 3877   g_string_free(self->auth_header_value, TRUE);
 3878   g_string_free(self->response_msg, TRUE);
 3879   g_string_free(self->connected_server, TRUE);
 3880   g_string_free(self->remote_server, TRUE);
 3881   g_string_free(self->request_url, TRUE);
 3882   http_destroy_url(&self->request_url_parts);
 3883   /* NOTE: hashes are freed up by pyvars */
 3884   z_poll_unref(self->poll);
 3885   z_proxy_free_method(s);
 3886   z_return();
 3887 }
 3888 
 3889 static ZProxy *
 3890 http_proxy_new(ZProxyParams *params)
 3891 {
 3892   HttpProxy *self;
 3893 
 3894   z_enter();
 3895   self = Z_CAST(z_proxy_new(Z_CLASS(HttpProxy), params), HttpProxy);
 3896   z_return((&self->super));
 3897 }
 3898 
 3899 static void http_proxy_free(ZObject *s);
 3900 
 3901 ZProxyFuncs http_proxy_funcs =
 3902   {
 3903     {
 3904       Z_FUNCS_COUNT(ZProxy),
 3905       http_proxy_free,
 3906     },           /* super */
 3907     http_config, /*config */
 3908     NULL,        /* startup */
 3909     http_main,   /* main */
 3910     NULL,        /* shutdown */
 3911     NULL,        /* destroy */
 3912     NULL,        /* nonblocking_init */
 3913     NULL,        /* nonblocking_deinit */
 3914     NULL,        /* wakeup */
 3915   };
 3916 
 3917 Z_CLASS_DEF(HttpProxy, ZProxy, http_proxy_funcs);
 3918 
 3919 static ZProxyModuleFuncs http_module_funcs =
 3920   {
 3921     /* .create_proxy = */ http_proxy_new,
 3922     /* .module_py_init = */ NULL,
 3923   };
 3924 
 3925 gint
 3926 zorp_module_init(void)
 3927 {
 3928   http_proto_init();
 3929   auth_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, http_auth_destroy);
 3930   z_registry_add("http", ZR_PROXY, &http_module_funcs);
 3931   return TRUE;
 3932 }