From a8bc83bea5f63d224b9b7686d1eb076c7efffbe7 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Fri, 30 Jan 2026 16:31:04 +0100 Subject: [PATCH] MINOR: cfgparse: move proxy post-init in a dedicated function A lot of proxies initialization code is delayed on post-parsing stage, as it depends on the configuration fully parsed. This is performed via a loop on proxies_list. Extract this code in a dedicated function proxy_finalize(). This patch will be useful for dynamic backends creation. Note that for the moment the code has been extracted as-is. With each new features, some init code was added there. This has become a giant loop with no real ordering. A future patch may provide some cleanup in order to reorganize this. --- include/haproxy/proxy.h | 1 + src/cfgparse.c | 1290 +------------------------------------- src/proxy.c | 1309 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 1315 insertions(+), 1285 deletions(-) diff --git a/include/haproxy/proxy.h b/include/haproxy/proxy.h index ed86925d2..afa6297c3 100644 --- a/include/haproxy/proxy.h +++ b/include/haproxy/proxy.h @@ -97,6 +97,7 @@ int resolve_stick_rule(struct proxy *curproxy, struct sticking_rule *mrule); void free_stick_rules(struct list *rules); void free_server_rules(struct list *srules); int proxy_init_per_thr(struct proxy *px); +int proxy_finalize(struct proxy *px, int *err_code); /* * This function returns a string containing the type of the proxy in a format diff --git a/src/cfgparse.c b/src/cfgparse.c index ea2eb32d9..39cabdf6b 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -2269,7 +2269,7 @@ err: */ int check_config_validity() { - int cfgerr = 0; + int cfgerr = 0, ret; struct proxy *init_proxies_list = NULL, *defpx; struct stktable *t; struct server *newsrv = NULL; @@ -2386,12 +2386,6 @@ int check_config_validity() init_proxies_list_stage1: for (curproxy = init_proxies_list; curproxy; curproxy = curproxy->next) { - struct switching_rule *rule; - struct server_rule *srule; - struct sticking_rule *mrule; - struct logger *tmplogger; - unsigned int next_id; - proxy_init_per_thr(curproxy); /* Assign automatic UUID if unset except for internal proxies. @@ -2426,1284 +2420,11 @@ init_proxies_list_stage1: continue; } - /* check and reduce the bind-proc of each listener */ - list_for_each_entry(bind_conf, &curproxy->conf.bind, by_fe) { - int mode = conn_pr_mode_to_proto_mode(curproxy->mode); - const struct mux_proto_list *mux_ent; - int ret; - - - /* Check the mux protocols, if any; before the check the ALPN */ - if (bind_conf->xprt && bind_conf->xprt == xprt_get(XPRT_QUIC)) { - if (!bind_conf->mux_proto) { - /* No protocol was specified. If we're using QUIC at the transport - * layer, we'll instantiate it as a mux as well. If QUIC is not - * compiled in, this will remain NULL. - */ - bind_conf->mux_proto = get_mux_proto(ist("quic")); - } - if (bind_conf->options & BC_O_ACC_PROXY) { - ha_alert("Binding [%s:%d] for %s %s: QUIC protocol does not support PROXY protocol yet." - " 'accept-proxy' option cannot be used with a QUIC listener.\n", - bind_conf->file, bind_conf->line, - proxy_type_str(curproxy), curproxy->id); - cfgerr++; - } - } - - if (bind_conf->mux_proto) { - /* it is possible that an incorrect mux was referenced - * due to the proxy's mode not being taken into account - * on first pass. Let's adjust it now. - */ - mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->token, PROTO_SIDE_FE, mode); - - if (!mux_ent || !isteq(mux_ent->token, bind_conf->mux_proto->token)) { - ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for 'bind %s' at [%s:%d].\n", - proxy_type_str(curproxy), curproxy->id, - (int)bind_conf->mux_proto->token.len, - bind_conf->mux_proto->token.ptr, - bind_conf->arg, bind_conf->file, bind_conf->line); - cfgerr++; - } else { - if ((mux_ent->mux->flags & MX_FL_FRAMED) && !(bind_conf->options & BC_O_USE_SOCK_DGRAM)) { - ha_alert("%s '%s' : frame-based MUX protocol '%.*s' is incompatible with stream transport of 'bind %s' at [%s:%d].\n", - proxy_type_str(curproxy), curproxy->id, - (int)bind_conf->mux_proto->token.len, - bind_conf->mux_proto->token.ptr, - bind_conf->arg, bind_conf->file, bind_conf->line); - cfgerr++; - } - else if (!(mux_ent->mux->flags & MX_FL_FRAMED) && !(bind_conf->options & BC_O_USE_SOCK_STREAM)) { - ha_alert("%s '%s' : stream-based MUX protocol '%.*s' is incompatible with framed transport of 'bind %s' at [%s:%d].\n", - proxy_type_str(curproxy), curproxy->id, - (int)bind_conf->mux_proto->token.len, - bind_conf->mux_proto->token.ptr, - bind_conf->arg, bind_conf->file, bind_conf->line); - cfgerr++; - } - } - - /* update the mux */ - bind_conf->mux_proto = mux_ent; - } - - - /* HTTP frontends with "h2" as ALPN/NPN will work in - * HTTP/2 and absolutely require buffers 16kB or larger. - */ -#ifdef USE_OPENSSL - /* no-alpn ? If so, it's the right moment to remove it */ - if (bind_conf->ssl_conf.alpn_str && !bind_conf->ssl_conf.alpn_len) { - ha_free(&bind_conf->ssl_conf.alpn_str); - } -#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation - else if (!bind_conf->ssl_conf.alpn_str && !bind_conf->ssl_conf.npn_str && - ((bind_conf->options & BC_O_USE_SSL) || bind_conf->xprt == xprt_get(XPRT_QUIC)) && - curproxy->mode == PR_MODE_HTTP && global.tune.bufsize >= 16384) { - - /* Neither ALPN nor NPN were explicitly set nor disabled, we're - * in HTTP mode with an SSL or QUIC listener, we can enable ALPN. - * Note that it's in binary form. First we try to set the ALPN from - * mux proto if set. Otherwise rely on the default ALPN. - */ - if (bind_conf->mux_proto && bind_conf->mux_proto->alpn) - bind_conf->ssl_conf.alpn_str = strdup(bind_conf->mux_proto->alpn); - else if (bind_conf->xprt == xprt_get(XPRT_QUIC)) - bind_conf->ssl_conf.alpn_str = strdup("\002h3"); - else - bind_conf->ssl_conf.alpn_str = strdup("\002h2\010http/1.1"); - - if (!bind_conf->ssl_conf.alpn_str) { - ha_alert("Proxy '%s': out of memory while trying to allocate a default alpn string in 'bind %s' at [%s:%d].\n", - curproxy->id, bind_conf->arg, bind_conf->file, bind_conf->line); - cfgerr++; - err_code |= ERR_FATAL | ERR_ALERT; - goto out; - } - bind_conf->ssl_conf.alpn_len = strlen(bind_conf->ssl_conf.alpn_str); - } -#endif - - if (curproxy->mode == PR_MODE_HTTP && global.tune.bufsize < 16384) { -#ifdef OPENSSL_NPN_NEGOTIATED - /* check NPN */ - if (bind_conf->ssl_conf.npn_str && strstr(bind_conf->ssl_conf.npn_str, "\002h2")) { - ha_alert("HTTP frontend '%s' enables HTTP/2 via NPN at [%s:%d], so global.tune.bufsize must be at least 16384 bytes (%d now).\n", - curproxy->id, bind_conf->file, bind_conf->line, global.tune.bufsize); - cfgerr++; - } -#endif -#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation - /* check ALPN */ - if (bind_conf->ssl_conf.alpn_str && strstr(bind_conf->ssl_conf.alpn_str, "\002h2")) { - ha_alert("HTTP frontend '%s' enables HTTP/2 via ALPN at [%s:%d], so global.tune.bufsize must be at least 16384 bytes (%d now).\n", - curproxy->id, bind_conf->file, bind_conf->line, global.tune.bufsize); - cfgerr++; - } -#endif - } /* HTTP && bufsize < 16384 */ -#endif - -#ifdef USE_QUIC - if (bind_conf->xprt == xprt_get(XPRT_QUIC)) { - const struct quic_cc_algo *cc_algo = bind_conf->quic_cc_algo ? - bind_conf->quic_cc_algo : default_quic_cc_algo; - - if (!(cc_algo->flags & QUIC_CC_ALGO_FL_OPT_PACING) && - !(quic_tune.fe.fb_opts & QUIC_TUNE_FB_TX_PACING)) { - ha_warning("Binding [%s:%d] for %s %s: using the selected congestion algorithm without pacing may cause slowdowns or high loss rates during transfers.\n", - bind_conf->file, bind_conf->line, - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - } -#endif /* USE_QUIC */ - - /* finish the bind setup */ - ret = bind_complete_thread_setup(bind_conf, &err_code); - if (ret != 0) { - cfgerr += ret; - if (err_code & ERR_FATAL) - goto out; - } - - if (bind_generate_guid(bind_conf)) { - cfgerr++; - err_code |= ERR_FATAL | ERR_ALERT; - goto out; - } - } - - switch (curproxy->mode) { - case PR_MODE_TCP: - cfgerr += proxy_cfg_ensure_no_http(curproxy); - cfgerr += proxy_cfg_ensure_no_log(curproxy); - break; - - case PR_MODE_HTTP: - cfgerr += proxy_cfg_ensure_no_log(curproxy); - curproxy->http_needed = 1; - break; - - case PR_MODE_CLI: - cfgerr += proxy_cfg_ensure_no_http(curproxy); - cfgerr += proxy_cfg_ensure_no_log(curproxy); - break; - - case PR_MODE_SYSLOG: - /* this mode is initialized as the classic tcp proxy */ - cfgerr += proxy_cfg_ensure_no_http(curproxy); - break; - - case PR_MODE_SPOP: - cfgerr += proxy_cfg_ensure_no_http(curproxy); - cfgerr += proxy_cfg_ensure_no_log(curproxy); - break; - - case PR_MODE_PEERS: - case PR_MODES: - /* should not happen, bug gcc warn missing switch statement */ - ha_alert("%s '%s' cannot initialize this proxy mode (peers) in this way. NOTE: PLEASE REPORT THIS TO DEVELOPERS AS YOU'RE NOT SUPPOSED TO BE ABLE TO CREATE A CONFIGURATION TRIGGERING THIS!\n", - proxy_type_str(curproxy), curproxy->id); - cfgerr++; - break; - } - - if (!(curproxy->cap & PR_CAP_INT) && (curproxy->cap & PR_CAP_FE) && LIST_ISEMPTY(&curproxy->conf.listeners)) { - ha_warning("%s '%s' has no 'bind' directive. Please declare it as a backend if this was intended.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - if (curproxy->cap & PR_CAP_BE) { - if (curproxy->lbprm.algo & BE_LB_KIND) { - if (curproxy->options & PR_O_TRANSP) { - ha_alert("%s '%s' cannot use both transparent and balance mode.\n", - proxy_type_str(curproxy), curproxy->id); - cfgerr++; - } - else if (curproxy->options & PR_O_DISPATCH) { - ha_warning("dispatch address of %s '%s' will be ignored in balance mode.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - } - else if (!(curproxy->options & (PR_O_TRANSP | PR_O_DISPATCH))) { - /* If no LB algo is set in a backend, and we're not in - * transparent mode, dispatch mode nor proxy mode, we - * want to use balance random by default. - */ - curproxy->lbprm.algo &= ~BE_LB_ALGO; - curproxy->lbprm.algo |= BE_LB_ALGO_RND; - } - } - - if (curproxy->options & PR_O_DISPATCH) - curproxy->options &= ~PR_O_TRANSP; - else if (curproxy->options & PR_O_TRANSP) - curproxy->options &= ~PR_O_DISPATCH; - - if ((curproxy->tcpcheck_rules.flags & TCPCHK_RULES_UNUSED_HTTP_RS)) { - ha_warning("%s '%s' uses http-check rules without 'option httpchk', so the rules are ignored.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - if ((curproxy->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK && - (curproxy->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_HTTP_CHK) { - if (curproxy->options & PR_O_DISABLE404) { - ha_warning("'%s' will be ignored for %s '%s' (requires 'option httpchk').\n", - "disable-on-404", proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - curproxy->options &= ~PR_O_DISABLE404; - } - if (curproxy->options2 & PR_O2_CHK_SNDST) { - ha_warning("'%s' will be ignored for %s '%s' (requires 'option httpchk').\n", - "send-state", proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - curproxy->options2 &= ~PR_O2_CHK_SNDST; - } - } - - if ((curproxy->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) { - if (!global.external_check) { - ha_alert("Proxy '%s' : '%s' unable to find required 'global.external-check'.\n", - curproxy->id, "option external-check"); - cfgerr++; - } - if (!curproxy->check_command) { - ha_alert("Proxy '%s' : '%s' unable to find required 'external-check command'.\n", - curproxy->id, "option external-check"); - cfgerr++; - } - if (!(global.tune.options & GTUNE_INSECURE_FORK)) { - ha_warning("Proxy '%s' : 'insecure-fork-wanted' not enabled in the global section, '%s' will likely fail.\n", - curproxy->id, "option external-check"); - err_code |= ERR_WARN; - } - } - - if (curproxy->email_alert.flags & PR_EMAIL_ALERT_SET) { - if (!(curproxy->email_alert.mailers.name && curproxy->email_alert.from && curproxy->email_alert.to)) { - ha_warning("'email-alert' will be ignored for %s '%s' (the presence any of " - "'email-alert from', 'email-alert level' 'email-alert mailers', " - "'email-alert myhostname', or 'email-alert to' " - "requires each of 'email-alert from', 'email-alert mailers' and 'email-alert to' " - "to be present).\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - free_email_alert(curproxy); - } - if (!curproxy->email_alert.myhostname) - curproxy->email_alert.myhostname = strdup(hostname); - } - - if (curproxy->check_command) { - int clear = 0; - if ((curproxy->options2 & PR_O2_CHK_ANY) != PR_O2_EXT_CHK) { - ha_warning("'%s' will be ignored for %s '%s' (requires 'option external-check').\n", - "external-check command", proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - clear = 1; - } - if (curproxy->check_command[0] != '/' && !curproxy->check_path) { - ha_alert("Proxy '%s': '%s' does not have a leading '/' and 'external-check path' is not set.\n", - curproxy->id, "external-check command"); - cfgerr++; - } - if (clear) { - ha_free(&curproxy->check_command); - } - } - - if (curproxy->check_path) { - if ((curproxy->options2 & PR_O2_CHK_ANY) != PR_O2_EXT_CHK) { - ha_warning("'%s' will be ignored for %s '%s' (requires 'option external-check').\n", - "external-check path", proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - ha_free(&curproxy->check_path); - } - } - - /* if a default backend was specified, let's find it */ - if (curproxy->defbe.name) { - struct proxy *target; - - target = proxy_be_by_name(curproxy->defbe.name); - if (!target) { - ha_alert("Proxy '%s': unable to find required default_backend: '%s'.\n", - curproxy->id, curproxy->defbe.name); - cfgerr++; - } else if (target == curproxy) { - ha_alert("Proxy '%s': loop detected for default_backend: '%s'.\n", - curproxy->id, curproxy->defbe.name); - cfgerr++; - } else if (target->mode != curproxy->mode && - !(curproxy->mode == PR_MODE_TCP && target->mode == PR_MODE_HTTP)) { - - ha_alert("%s %s '%s' (%s:%d) tries to use incompatible %s %s '%s' (%s:%d) as its default backend (see 'mode').\n", - proxy_mode_str(curproxy->mode), proxy_type_str(curproxy), curproxy->id, - curproxy->conf.file, curproxy->conf.line, - proxy_mode_str(target->mode), proxy_type_str(target), target->id, - target->conf.file, target->conf.line); - cfgerr++; - } else { - free(curproxy->defbe.name); - curproxy->defbe.be = target; - /* Emit a warning if this proxy also has some servers */ - if (curproxy->srv) { - ha_warning("In proxy '%s', the 'default_backend' rule always has precedence over the servers, which will never be used.\n", - curproxy->id); - err_code |= ERR_WARN; - } - if (target->mode == PR_MODE_HTTP) { - /* at least one of the used backends will provoke an - * HTTP upgrade - */ - curproxy->options |= PR_O_HTTP_UPG; - } - } - } - - /* find the target proxy for 'use_backend' rules */ - list_for_each_entry(rule, &curproxy->switching_rules, list) { - struct proxy *target; - struct logformat_node *node; - char *pxname; - - /* Try to parse the string as a log format expression. If the result - * of the parsing is only one entry containing a simple string, then - * it's a standard string corresponding to a static rule, thus the - * parsing is cancelled and be.name is restored to be resolved. - */ - pxname = rule->be.name; - lf_expr_init(&rule->be.expr); - curproxy->conf.args.ctx = ARGC_UBK; - curproxy->conf.args.file = rule->file; - curproxy->conf.args.line = rule->line; - err = NULL; - if (!parse_logformat_string(pxname, curproxy, &rule->be.expr, 0, SMP_VAL_FE_HRQ_HDR, &err)) { - ha_alert("Parsing [%s:%d]: failed to parse use_backend rule '%s' : %s.\n", - rule->file, rule->line, pxname, err); - free(err); - cfgerr++; - continue; - } - node = LIST_NEXT(&rule->be.expr.nodes.list, struct logformat_node *, list); - - if (!lf_expr_isempty(&rule->be.expr)) { - if (node->type != LOG_FMT_TEXT || node->list.n != &rule->be.expr.nodes.list) { - rule->dynamic = 1; - free(pxname); - /* backend is not yet known so we cannot assume its type, - * thus we should consider that at least one of the used - * backends may provoke HTTP upgrade - */ - curproxy->options |= PR_O_HTTP_UPG; - continue; - } - /* Only one element in the list, a simple string: free the expression and - * fall back to static rule - */ - lf_expr_deinit(&rule->be.expr); - } - - rule->dynamic = 0; - rule->be.name = pxname; - - target = proxy_be_by_name(rule->be.name); - if (!target) { - ha_alert("Proxy '%s': unable to find required use_backend: '%s'.\n", - curproxy->id, rule->be.name); - cfgerr++; - } else if (target == curproxy) { - ha_alert("Proxy '%s': loop detected for use_backend: '%s'.\n", - curproxy->id, rule->be.name); - cfgerr++; - } else if (target->mode != curproxy->mode && - !(curproxy->mode == PR_MODE_TCP && target->mode == PR_MODE_HTTP)) { - - ha_alert("%s %s '%s' (%s:%d) tries to use incompatible %s %s '%s' (%s:%d) in a 'use_backend' rule (see 'mode').\n", - proxy_mode_str(curproxy->mode), proxy_type_str(curproxy), curproxy->id, - curproxy->conf.file, curproxy->conf.line, - proxy_mode_str(target->mode), proxy_type_str(target), target->id, - target->conf.file, target->conf.line); - cfgerr++; - } else { - ha_free(&rule->be.name); - rule->be.backend = target; - if (target->mode == PR_MODE_HTTP) { - /* at least one of the used backends will provoke an - * HTTP upgrade - */ - curproxy->options |= PR_O_HTTP_UPG; - } - } - err_code |= warnif_tcp_http_cond(curproxy, rule->cond); - } - - /* find the target server for 'use_server' rules */ - list_for_each_entry(srule, &curproxy->server_rules, list) { - struct server *target; - struct logformat_node *node; - char *server_name; - - /* We try to parse the string as a log format expression. If the result of the parsing - * is only one entry containing a single string, then it's a standard string corresponding - * to a static rule, thus the parsing is cancelled and we fall back to setting srv.ptr. - */ - server_name = srule->srv.name; - lf_expr_init(&srule->expr); - curproxy->conf.args.ctx = ARGC_USRV; - err = NULL; - if (!parse_logformat_string(server_name, curproxy, &srule->expr, 0, SMP_VAL_FE_HRQ_HDR, &err)) { - ha_alert("Parsing [%s:%d]; use-server rule failed to parse log-format '%s' : %s.\n", - srule->file, srule->line, server_name, err); - free(err); - cfgerr++; - continue; - } - node = LIST_NEXT(&srule->expr.nodes.list, struct logformat_node *, list); - - if (!lf_expr_isempty(&srule->expr)) { - if (node->type != LOG_FMT_TEXT || node->list.n != &srule->expr.nodes.list) { - srule->dynamic = 1; - free(server_name); - continue; - } - /* Only one element in the list, a simple string: free the expression and - * fall back to static rule - */ - lf_expr_deinit(&srule->expr); - } - - srule->dynamic = 0; - srule->srv.name = server_name; - target = server_find_by_name(curproxy, srule->srv.name); - err_code |= warnif_tcp_http_cond(curproxy, srule->cond); - - if (!target) { - ha_alert("%s '%s' : unable to find server '%s' referenced in a 'use-server' rule.\n", - proxy_type_str(curproxy), curproxy->id, srule->srv.name); - cfgerr++; - continue; - } - ha_free(&srule->srv.name); - srule->srv.ptr = target; - target->flags |= SRV_F_NON_PURGEABLE; - } - - /* find the target table for 'stick' rules */ - list_for_each_entry(mrule, &curproxy->sticking_rules, list) { - curproxy->be_req_ana |= AN_REQ_STICKING_RULES; - if (mrule->flags & STK_IS_STORE) - curproxy->be_rsp_ana |= AN_RES_STORE_RULES; - - if (!resolve_stick_rule(curproxy, mrule)) - cfgerr++; - - err_code |= warnif_tcp_http_cond(curproxy, mrule->cond); - } - - /* find the target table for 'store response' rules */ - list_for_each_entry(mrule, &curproxy->storersp_rules, list) { - curproxy->be_rsp_ana |= AN_RES_STORE_RULES; - - if (!resolve_stick_rule(curproxy, mrule)) - cfgerr++; - } - - /* check validity for 'tcp-request' layer 4/5/6/7 rules */ - cfgerr += check_action_rules(&curproxy->tcp_req.l4_rules, curproxy, &err_code); - cfgerr += check_action_rules(&curproxy->tcp_req.l5_rules, curproxy, &err_code); - cfgerr += check_action_rules(&curproxy->tcp_req.inspect_rules, curproxy, &err_code); - cfgerr += check_action_rules(&curproxy->tcp_rep.inspect_rules, curproxy, &err_code); - cfgerr += check_action_rules(&curproxy->http_req_rules, curproxy, &err_code); - cfgerr += check_action_rules(&curproxy->http_res_rules, curproxy, &err_code); - cfgerr += check_action_rules(&curproxy->http_after_res_rules, curproxy, &err_code); - - /* Warn is a switch-mode http is used on a TCP listener with servers but no backend */ - if (!curproxy->defbe.name && LIST_ISEMPTY(&curproxy->switching_rules) && curproxy->srv) { - if ((curproxy->options & PR_O_HTTP_UPG) && curproxy->mode == PR_MODE_TCP) - ha_warning("Proxy '%s' : 'switch-mode http' configured for a %s %s with no backend. " - "Incoming connections upgraded to HTTP cannot be routed to TCP servers\n", - curproxy->id, proxy_mode_str(curproxy->mode), proxy_type_str(curproxy)); - } - - if (curproxy->table && curproxy->table->peers.name) { - struct peers *curpeers; - - for (curpeers = cfg_peers; curpeers; curpeers = curpeers->next) { - if (strcmp(curpeers->id, curproxy->table->peers.name) == 0) { - ha_free(&curproxy->table->peers.name); - curproxy->table->peers.p = curpeers; - break; - } - } - - if (!curpeers) { - ha_alert("Proxy '%s': unable to find sync peers '%s'.\n", - curproxy->id, curproxy->table->peers.name); - ha_free(&curproxy->table->peers.name); - curproxy->table->peers.p = NULL; - cfgerr++; - } - else if (curpeers->disabled) { - /* silently disable this peers section */ - curproxy->table->peers.p = NULL; - } - else if (!curpeers->peers_fe) { - ha_alert("Proxy '%s': unable to find local peer '%s' in peers section '%s'.\n", - curproxy->id, localpeer, curpeers->id); - curproxy->table->peers.p = NULL; - cfgerr++; - } - } - - - if (curproxy->email_alert.mailers.name) { - struct mailers *curmailers = mailers; - - for (curmailers = mailers; curmailers; curmailers = curmailers->next) { - if (strcmp(curmailers->id, curproxy->email_alert.mailers.name) == 0) - break; - } - if (!curmailers) { - ha_alert("Proxy '%s': unable to find mailers '%s'.\n", - curproxy->id, curproxy->email_alert.mailers.name); - free_email_alert(curproxy); - cfgerr++; - } - else { - err = NULL; - if (init_email_alert(curmailers, curproxy, &err)) { - ha_alert("Proxy '%s': %s.\n", curproxy->id, err); - free(err); - cfgerr++; - } - } - } - - if (curproxy->uri_auth && !(curproxy->uri_auth->flags & STAT_F_CONVDONE) && - !LIST_ISEMPTY(&curproxy->uri_auth->http_req_rules) && - (curproxy->uri_auth->userlist || curproxy->uri_auth->auth_realm )) { - ha_alert("%s '%s': stats 'auth'/'realm' and 'http-request' can't be used at the same time.\n", - "proxy", curproxy->id); - cfgerr++; - goto out_uri_auth_compat; - } - - if (curproxy->uri_auth && curproxy->uri_auth->userlist && - (!(curproxy->uri_auth->flags & STAT_F_CONVDONE) || - LIST_ISEMPTY(&curproxy->uri_auth->http_req_rules))) { - const char *uri_auth_compat_req[10]; - struct act_rule *rule; - i = 0; - - /* build the ACL condition from scratch. We're relying on anonymous ACLs for that */ - uri_auth_compat_req[i++] = "auth"; - - if (curproxy->uri_auth->auth_realm) { - uri_auth_compat_req[i++] = "realm"; - uri_auth_compat_req[i++] = curproxy->uri_auth->auth_realm; - } - - uri_auth_compat_req[i++] = "unless"; - uri_auth_compat_req[i++] = "{"; - uri_auth_compat_req[i++] = "http_auth(.internal-stats-userlist)"; - uri_auth_compat_req[i++] = "}"; - uri_auth_compat_req[i++] = ""; - - rule = parse_http_req_cond(uri_auth_compat_req, "internal-stats-auth-compat", 0, curproxy); - if (!rule) { - cfgerr++; - break; - } - - LIST_APPEND(&curproxy->uri_auth->http_req_rules, &rule->list); - - if (curproxy->uri_auth->auth_realm) { - ha_free(&curproxy->uri_auth->auth_realm); - } - curproxy->uri_auth->flags |= STAT_F_CONVDONE; - } -out_uri_auth_compat: - - /* check whether we have a logger that uses RFC5424 log format */ - list_for_each_entry(tmplogger, &curproxy->loggers, list) { - if (tmplogger->format == LOG_FORMAT_RFC5424) { - if (!curproxy->logformat_sd.str) { - /* set the default logformat_sd_string */ - curproxy->logformat_sd.str = default_rfc5424_sd_log_format; - } - break; - } - } - - /* compile the log format */ - if (!(curproxy->cap & PR_CAP_FE)) { - lf_expr_deinit(&curproxy->logformat); - lf_expr_deinit(&curproxy->logformat_sd); - } - - if (curproxy->logformat.str) { - curproxy->conf.args.ctx = ARGC_LOG; - curproxy->conf.args.file = curproxy->logformat.conf.file; - curproxy->conf.args.line = curproxy->logformat.conf.line; - err = NULL; - if (!lf_expr_compile(&curproxy->logformat, &curproxy->conf.args, - LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES, - SMP_VAL_FE_LOG_END, &err) || - !lf_expr_postcheck(&curproxy->logformat, curproxy, &err)) { - ha_alert("Parsing [%s:%d]: failed to parse log-format : %s.\n", - curproxy->logformat.conf.file, curproxy->logformat.conf.line, err); - free(err); - cfgerr++; - } - curproxy->conf.args.file = NULL; - curproxy->conf.args.line = 0; - } - - if (curproxy->logformat_sd.str) { - curproxy->conf.args.ctx = ARGC_LOGSD; - curproxy->conf.args.file = curproxy->logformat_sd.conf.file; - curproxy->conf.args.line = curproxy->logformat_sd.conf.line; - err = NULL; - if (!lf_expr_compile(&curproxy->logformat_sd, &curproxy->conf.args, - LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES, - SMP_VAL_FE_LOG_END, &err) || - !add_to_logformat_list(NULL, NULL, LF_SEPARATOR, &curproxy->logformat_sd, &err) || - !lf_expr_postcheck(&curproxy->logformat_sd, curproxy, &err)) { - ha_alert("Parsing [%s:%d]: failed to parse log-format-sd : %s.\n", - curproxy->logformat_sd.conf.file, curproxy->logformat_sd.conf.line, err); - free(err); - cfgerr++; - } - curproxy->conf.args.file = NULL; - curproxy->conf.args.line = 0; - } - - if (curproxy->format_unique_id.str) { - int where = 0; - - curproxy->conf.args.ctx = ARGC_UIF; - curproxy->conf.args.file = curproxy->format_unique_id.conf.file; - curproxy->conf.args.line = curproxy->format_unique_id.conf.line; - err = NULL; - if (curproxy->cap & PR_CAP_FE) - where |= SMP_VAL_FE_HRQ_HDR; - if (curproxy->cap & PR_CAP_BE) - where |= SMP_VAL_BE_HRQ_HDR; - if (!lf_expr_compile(&curproxy->format_unique_id, &curproxy->conf.args, - LOG_OPT_HTTP|LOG_OPT_MERGE_SPACES, where, &err) || - !lf_expr_postcheck(&curproxy->format_unique_id, curproxy, &err)) { - ha_alert("Parsing [%s:%d]: failed to parse unique-id : %s.\n", - curproxy->format_unique_id.conf.file, curproxy->format_unique_id.conf.line, err); - free(err); - cfgerr++; - } - curproxy->conf.args.file = NULL; - curproxy->conf.args.line = 0; - } - - if (curproxy->logformat_error.str) { - curproxy->conf.args.ctx = ARGC_LOG; - curproxy->conf.args.file = curproxy->logformat_error.conf.file; - curproxy->conf.args.line = curproxy->logformat_error.conf.line; - err = NULL; - if (!lf_expr_compile(&curproxy->logformat_error, &curproxy->conf.args, - LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES, - SMP_VAL_FE_LOG_END, &err) || - !lf_expr_postcheck(&curproxy->logformat_error, curproxy, &err)) { - ha_alert("Parsing [%s:%d]: failed to parse error-log-format : %s.\n", - curproxy->logformat_error.conf.file, curproxy->logformat_error.conf.line, err); - free(err); - cfgerr++; - } - curproxy->conf.args.file = NULL; - curproxy->conf.args.line = 0; - } - - /* "balance hash" needs to compile its expression - * (log backends will handle this in proxy log postcheck) - */ - if (curproxy->mode != PR_MODE_SYSLOG && - (curproxy->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_SMP) { - int idx = 0; - const char *args[] = { - curproxy->lbprm.arg_str, - NULL, - }; - - err = NULL; - curproxy->conf.args.ctx = ARGC_USRV; // same context as use_server. - curproxy->lbprm.expr = - sample_parse_expr((char **)args, &idx, - curproxy->conf.file, curproxy->conf.line, - &err, &curproxy->conf.args, NULL); - - if (!curproxy->lbprm.expr) { - ha_alert("%s '%s' [%s:%d]: failed to parse 'balance hash' expression '%s' in : %s.\n", - proxy_type_str(curproxy), curproxy->id, - curproxy->conf.file, curproxy->conf.line, - curproxy->lbprm.arg_str, err); - ha_free(&err); - cfgerr++; - } - else if (!(curproxy->lbprm.expr->fetch->val & SMP_VAL_BE_SET_SRV)) { - ha_alert("%s '%s' [%s:%d]: error detected while parsing 'balance hash' expression '%s' " - "which requires information from %s, which is not available here.\n", - proxy_type_str(curproxy), curproxy->id, - curproxy->conf.file, curproxy->conf.line, - curproxy->lbprm.arg_str, sample_src_names(curproxy->lbprm.expr->fetch->use)); - cfgerr++; - } - else if (curproxy->mode == PR_MODE_HTTP && (curproxy->lbprm.expr->fetch->use & SMP_USE_L6REQ)) { - ha_warning("%s '%s' [%s:%d]: L6 sample fetch <%s> will be ignored in 'balance hash' expression in HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id, - curproxy->conf.file, curproxy->conf.line, - curproxy->lbprm.arg_str); - } - else - curproxy->http_needed |= !!(curproxy->lbprm.expr->fetch->use & SMP_USE_HTTP_ANY); - } - - /* only now we can check if some args remain unresolved. - * This must be done after the users and groups resolution. - */ - err = NULL; - i = smp_resolve_args(curproxy, &err); - cfgerr += i; - if (i) { - indent_msg(&err, 8); - ha_alert("%s%s\n", i > 1 ? "multiple argument resolution errors:" : "", err); - ha_free(&err); - } else - cfgerr += acl_find_targets(curproxy); - - if (!(curproxy->cap & PR_CAP_INT) && (curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HTTP) && - (((curproxy->cap & PR_CAP_FE) && !curproxy->timeout.client) || - ((curproxy->cap & PR_CAP_BE) && (curproxy->srv) && - (!curproxy->timeout.connect || - (!curproxy->timeout.server && (curproxy->mode == PR_MODE_HTTP || !curproxy->timeout.tunnel)))))) { - ha_warning("missing timeouts for %s '%s'.\n" - " | While not properly invalid, you will certainly encounter various problems\n" - " | with such a configuration. To fix this, please ensure that all following\n" - " | timeouts are set to a non-zero value: 'client', 'connect', 'server'.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - /* Historically, the tarpit and queue timeouts were inherited from contimeout. - * We must still support older configurations, so let's find out whether those - * parameters have been set or must be copied from contimeouts. - */ - if (!curproxy->timeout.tarpit) - curproxy->timeout.tarpit = curproxy->timeout.connect; - if ((curproxy->cap & PR_CAP_BE) && !curproxy->timeout.queue) - curproxy->timeout.queue = curproxy->timeout.connect; - - if ((curproxy->tcpcheck_rules.flags & TCPCHK_RULES_UNUSED_TCP_RS)) { - ha_warning("%s '%s' uses tcp-check rules without 'option tcp-check', so the rules are ignored.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - /* ensure that cookie capture length is not too large */ - if (curproxy->capture_len >= global.tune.cookie_len) { - ha_warning("truncating capture length to %d bytes for %s '%s'.\n", - global.tune.cookie_len - 1, proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - curproxy->capture_len = global.tune.cookie_len - 1; - } - - /* The small pools required for the capture lists */ - if (curproxy->nb_req_cap) { - curproxy->req_cap_pool = create_pool("ptrcap", - curproxy->nb_req_cap * sizeof(char *), - MEM_F_SHARED); - } - - if (curproxy->nb_rsp_cap) { - curproxy->rsp_cap_pool = create_pool("ptrcap", - curproxy->nb_rsp_cap * sizeof(char *), - MEM_F_SHARED); - } - - switch (curproxy->load_server_state_from_file) { - case PR_SRV_STATE_FILE_UNSPEC: - curproxy->load_server_state_from_file = PR_SRV_STATE_FILE_NONE; - break; - case PR_SRV_STATE_FILE_GLOBAL: - if (!global.server_state_file) { - ha_warning("backend '%s' configured to load server state file from global section 'server-state-file' directive. Unfortunately, 'server-state-file' is not set!\n", - curproxy->id); - err_code |= ERR_WARN; - } - break; - } - - /* first, we will invert the servers list order */ - newsrv = NULL; - while (curproxy->srv) { - struct server *next; - - next = curproxy->srv->next; - curproxy->srv->next = newsrv; - newsrv = curproxy->srv; - if (!next) - break; - curproxy->srv = next; - } - - /* Check that no server name conflicts. This causes trouble in the stats. - * We only emit an error for the first conflict affecting each server, - * in order to avoid combinatory explosion if all servers have the same - * name. Since servers names are stored in a tree before landing here, - * we simply have to check for the current server's duplicates to spot - * conflicts. - */ - for (newsrv = curproxy->srv; newsrv; newsrv = newsrv->next) { - struct server *other_srv; - - /* Note: internal servers are not always registered and - * they do not conflict. - */ - if (!ceb_intree(&newsrv->conf.name_node)) - continue; - - for (other_srv = newsrv; - (other_srv = cebis_item_prev_dup(&curproxy->conf.used_server_name, conf.name_node, id, other_srv)); ) { - ha_alert("parsing [%s:%d] : %s '%s', another server named '%s' was already defined at line %d, please use distinct names.\n", - newsrv->conf.file, newsrv->conf.line, - proxy_type_str(curproxy), curproxy->id, - newsrv->id, other_srv->conf.line); - cfgerr++; - break; - } - } - - /* assign automatic UIDs to servers which don't have one yet */ - next_id = 1; - newsrv = curproxy->srv; - while (newsrv != NULL) { - if (!newsrv->puid) { - /* server ID not set, use automatic numbering with first - * spare entry starting with next_svid. - */ - next_id = server_get_next_id(curproxy, next_id); - newsrv->puid = next_id; - server_index_id(curproxy, newsrv); - } - - next_id++; - newsrv = newsrv->next; - } - - curproxy->lbprm.wmult = 1; /* default weight multiplier */ - curproxy->lbprm.wdiv = 1; /* default weight divider */ - - /* - * If this server supports a maxconn parameter, it needs a dedicated - * tasks to fill the emptied slots when a connection leaves. - * Also, resolve deferred tracking dependency if needed. - */ - newsrv = curproxy->srv; - while (newsrv != NULL) { - set_usermsgs_ctx(newsrv->conf.file, newsrv->conf.line, &newsrv->obj_type); - - srv_minmax_conn_apply(newsrv); - - /* this will also properly set the transport layer for - * prod and checks - * if default-server have use_ssl, prerare ssl init - * without activating it */ - if (newsrv->use_ssl == 1 || newsrv->check.use_ssl == 1 || - (newsrv->proxy->options & PR_O_TCPCHK_SSL) || - ((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) { - if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv) - cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv); - else if (xprt_get(XPRT_QUIC) && xprt_get(XPRT_QUIC)->prepare_srv) - cfgerr += xprt_get(XPRT_QUIC)->prepare_srv(newsrv); - } - - if (newsrv->use_ssl == 1 || ((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) { - /* In HTTP only, if the SNI is not set and we can rely on the host - * header value, fill the sni expression accordingly - */ - if (!newsrv->sni_expr && newsrv->proxy->mode == PR_MODE_HTTP && - !(newsrv->ssl_ctx.options & SRV_SSL_O_NO_AUTO_SNI)) { - newsrv->sni_expr = strdup("req.hdr(host),field(1,:)"); - - err = NULL; - if (server_parse_exprs(newsrv, curproxy, &err)) { - ha_alert("parsing [%s:%d]: failed to parse auto SNI expression: %s\n", - newsrv->conf.file, newsrv->conf.line, err); - free(err); - ++cfgerr; - goto next_srv; - } - } - } - - - if ((newsrv->flags & SRV_F_FASTOPEN) && - ((curproxy->retry_type & (PR_RE_DISCONNECTED | PR_RE_TIMEOUT)) != - (PR_RE_DISCONNECTED | PR_RE_TIMEOUT))) - ha_warning("server has tfo activated, the backend should be configured with at least 'conn-failure', 'empty-response' and 'response-timeout' or we wouldn't be able to retry the connection on failure.\n"); - - if (newsrv->trackit) { - if (srv_apply_track(newsrv, curproxy)) { - ++cfgerr; - goto next_srv; - } - } - - next_srv: - reset_usermsgs_ctx(); - newsrv = newsrv->next; - } - - /* - * Try to generate dynamic cookies for servers now. - * It couldn't be done earlier, since at the time we parsed - * the server line, we may not have known yet that we - * should use dynamic cookies, or the secret key may not - * have been provided yet. - */ - if (curproxy->ck_opts & PR_CK_DYNAMIC) { - newsrv = curproxy->srv; - while (newsrv != NULL) { - srv_set_dyncookie(newsrv); - newsrv = newsrv->next; - } - - } - /* We have to initialize the server lookup mechanism depending - * on what LB algorithm was chosen. - */ - - curproxy->lbprm.algo &= ~(BE_LB_LKUP | BE_LB_PROP_DYN); - switch (curproxy->lbprm.algo & BE_LB_KIND) { - case BE_LB_KIND_RR: - if ((curproxy->lbprm.algo & BE_LB_PARM) == BE_LB_RR_STATIC) { - curproxy->lbprm.algo |= BE_LB_LKUP_MAP; - init_server_map(curproxy); - } else if ((curproxy->lbprm.algo & BE_LB_PARM) == BE_LB_RR_RANDOM) { - curproxy->lbprm.algo |= BE_LB_LKUP_CHTREE | BE_LB_PROP_DYN; - if (chash_init_server_tree(curproxy) < 0) { - cfgerr++; - } - } else { - curproxy->lbprm.algo |= BE_LB_LKUP_RRTREE | BE_LB_PROP_DYN; - fwrr_init_server_groups(curproxy); - } - break; - - case BE_LB_KIND_CB: - if ((curproxy->lbprm.algo & BE_LB_PARM) == BE_LB_CB_LC) { - curproxy->lbprm.algo |= BE_LB_LKUP_LCTREE | BE_LB_PROP_DYN; - fwlc_init_server_tree(curproxy); - } else { - curproxy->lbprm.algo |= BE_LB_LKUP_FSTREE | BE_LB_PROP_DYN; - fas_init_server_tree(curproxy); - } - break; - - case BE_LB_KIND_HI: - if ((curproxy->lbprm.algo & BE_LB_HASH_TYPE) == BE_LB_HASH_CONS) { - curproxy->lbprm.algo |= BE_LB_LKUP_CHTREE | BE_LB_PROP_DYN; - if (chash_init_server_tree(curproxy) < 0) { - cfgerr++; - } - } else { - curproxy->lbprm.algo |= BE_LB_LKUP_MAP; - init_server_map(curproxy); - } - break; - case BE_LB_KIND_SA: - if ((curproxy->lbprm.algo & BE_LB_PARM) == BE_LB_SA_SS) { - curproxy->lbprm.algo |= BE_LB_PROP_DYN; - init_server_ss(curproxy); - } - break; - } - HA_RWLOCK_INIT(&curproxy->lbprm.lock); - - if (curproxy->options & PR_O_LOGASAP) - curproxy->to_log &= ~LW_BYTES; - - if (!(curproxy->cap & PR_CAP_INT) && (curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HTTP) && - (curproxy->cap & PR_CAP_FE) && LIST_ISEMPTY(&curproxy->loggers) && - (!lf_expr_isempty(&curproxy->logformat) || !lf_expr_isempty(&curproxy->logformat_sd))) { - ha_warning("log format ignored for %s '%s' since it has no log address.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - if (curproxy->mode != PR_MODE_HTTP && !(curproxy->options & PR_O_HTTP_UPG)) { - int optnum; - - if (curproxy->uri_auth) { - ha_warning("'stats' statement ignored for %s '%s' as it requires HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - stats_uri_auth_drop(curproxy->uri_auth); - curproxy->uri_auth = NULL; - } - - if (curproxy->capture_name) { - ha_warning("'capture' statement ignored for %s '%s' as it requires HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - if (isttest(curproxy->monitor_uri)) { - ha_warning("'monitor-uri' statement ignored for %s '%s' as it requires HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - if (!LIST_ISEMPTY(&curproxy->http_req_rules)) { - ha_warning("'http-request' rules ignored for %s '%s' as they require HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - if (!LIST_ISEMPTY(&curproxy->http_res_rules)) { - ha_warning("'http-response' rules ignored for %s '%s' as they require HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - if (!LIST_ISEMPTY(&curproxy->http_after_res_rules)) { - ha_warning("'http-after-response' rules ignored for %s '%s' as they require HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - if (!LIST_ISEMPTY(&curproxy->redirect_rules)) { - ha_warning("'redirect' rules ignored for %s '%s' as they require HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - } - - for (optnum = 0; cfg_opts[optnum].name; optnum++) { - if (cfg_opts[optnum].mode == PR_MODE_HTTP && - (curproxy->cap & cfg_opts[optnum].cap) && - (curproxy->options & cfg_opts[optnum].val)) { - ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", - cfg_opts[optnum].name, proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - curproxy->options &= ~cfg_opts[optnum].val; - } - } - - for (optnum = 0; cfg_opts2[optnum].name; optnum++) { - if (cfg_opts2[optnum].mode == PR_MODE_HTTP && - (curproxy->cap & cfg_opts2[optnum].cap) && - (curproxy->options2 & cfg_opts2[optnum].val)) { - ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", - cfg_opts2[optnum].name, proxy_type_str(curproxy), curproxy->id); - err_code |= ERR_WARN; - curproxy->options2 &= ~cfg_opts2[optnum].val; - } - } - -#if defined(CONFIG_HAP_TRANSPARENT) - if (curproxy->conn_src.bind_hdr_occ) { - curproxy->conn_src.bind_hdr_occ = 0; - ha_warning("%s '%s' : ignoring use of header %s as source IP in non-HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id, curproxy->conn_src.bind_hdr_name); - err_code |= ERR_WARN; - } -#endif - } - - /* - * ensure that we're not cross-dressing a TCP server into HTTP. - */ - newsrv = curproxy->srv; - while (newsrv != NULL) { - if ((curproxy->mode != PR_MODE_HTTP) && newsrv->rdr_len) { - ha_alert("%s '%s' : server cannot have cookie or redirect prefix in non-HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id); - cfgerr++; - } - - if ((curproxy->mode != PR_MODE_HTTP) && newsrv->cklen) { - ha_warning("%s '%s' : ignoring cookie for server '%s' as HTTP mode is disabled.\n", - proxy_type_str(curproxy), curproxy->id, newsrv->id); - err_code |= ERR_WARN; - } - - if ((newsrv->flags & SRV_F_MAPPORTS) && (curproxy->options2 & PR_O2_RDPC_PRST)) { - ha_warning("%s '%s' : RDP cookie persistence will not work for server '%s' because it lacks an explicit port number.\n", - proxy_type_str(curproxy), curproxy->id, newsrv->id); - err_code |= ERR_WARN; - } - -#if defined(CONFIG_HAP_TRANSPARENT) - if (curproxy->mode != PR_MODE_HTTP && newsrv->conn_src.bind_hdr_occ) { - newsrv->conn_src.bind_hdr_occ = 0; - ha_warning("%s '%s' : server %s cannot use header %s as source IP in non-HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id, newsrv->id, newsrv->conn_src.bind_hdr_name); - err_code |= ERR_WARN; - } -#endif - - if ((curproxy->mode != PR_MODE_HTTP) && (curproxy->options & PR_O_REUSE_MASK) != PR_O_REUSE_NEVR) - curproxy->options &= ~PR_O_REUSE_MASK; - if (curproxy->mode == PR_MODE_SPOP) - curproxy->options |= PR_O_REUSE_ALWS; - - if ((curproxy->mode != PR_MODE_HTTP) && newsrv->flags & SRV_F_RHTTP) { - ha_alert("%s '%s' : server %s uses reverse HTTP addressing which can only be used with HTTP mode.\n", - proxy_type_str(curproxy), curproxy->id, newsrv->id); - cfgerr++; - err_code |= ERR_FATAL | ERR_ALERT; + ret = proxy_finalize(curproxy, &err_code); + if (ret) { + cfgerr += ret; + if (err_code & ERR_FATAL) goto out; - } - - newsrv = newsrv->next; - } - - /* Check filter configuration, if any */ - cfgerr += flt_check(curproxy); - - if (curproxy->cap & PR_CAP_FE) { - if (!curproxy->accept) - curproxy->accept = frontend_accept; - - if (!LIST_ISEMPTY(&curproxy->tcp_req.inspect_rules) || - (curproxy->defpx && !LIST_ISEMPTY(&curproxy->defpx->tcp_req.inspect_rules))) - curproxy->fe_req_ana |= AN_REQ_INSPECT_FE; - - if (curproxy->mode == PR_MODE_HTTP) { - curproxy->fe_req_ana |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_PROCESS_FE; - curproxy->fe_rsp_ana |= AN_RES_WAIT_HTTP | AN_RES_HTTP_PROCESS_FE; - } - - if (curproxy->mode == PR_MODE_CLI) { - curproxy->fe_req_ana |= AN_REQ_WAIT_CLI; - curproxy->fe_rsp_ana |= AN_RES_WAIT_CLI; - } - - /* both TCP and HTTP must check switching rules */ - curproxy->fe_req_ana |= AN_REQ_SWITCHING_RULES; - - /* Add filters analyzers if needed */ - if (!LIST_ISEMPTY(&curproxy->filter_configs)) { - curproxy->fe_req_ana |= AN_REQ_FLT_START_FE | AN_REQ_FLT_XFER_DATA | AN_REQ_FLT_END; - curproxy->fe_rsp_ana |= AN_RES_FLT_START_FE | AN_RES_FLT_XFER_DATA | AN_RES_FLT_END; - } - } - - if (curproxy->cap & PR_CAP_BE) { - if (!LIST_ISEMPTY(&curproxy->tcp_req.inspect_rules) || - (curproxy->defpx && !LIST_ISEMPTY(&curproxy->defpx->tcp_req.inspect_rules))) - curproxy->be_req_ana |= AN_REQ_INSPECT_BE; - - if (!LIST_ISEMPTY(&curproxy->tcp_rep.inspect_rules) || - (curproxy->defpx && !LIST_ISEMPTY(&curproxy->defpx->tcp_rep.inspect_rules))) - curproxy->be_rsp_ana |= AN_RES_INSPECT; - - if (curproxy->mode == PR_MODE_HTTP) { - curproxy->be_req_ana |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_INNER | AN_REQ_HTTP_PROCESS_BE; - curproxy->be_rsp_ana |= AN_RES_WAIT_HTTP | AN_RES_HTTP_PROCESS_BE; - } - - /* If the backend does requires RDP cookie persistence, we have to - * enable the corresponding analyser. - */ - if (curproxy->options2 & PR_O2_RDPC_PRST) - curproxy->be_req_ana |= AN_REQ_PRST_RDP_COOKIE; - - /* Add filters analyzers if needed */ - if (!LIST_ISEMPTY(&curproxy->filter_configs)) { - curproxy->be_req_ana |= AN_REQ_FLT_START_BE | AN_REQ_FLT_XFER_DATA | AN_REQ_FLT_END; - curproxy->be_rsp_ana |= AN_RES_FLT_START_BE | AN_RES_FLT_XFER_DATA | AN_RES_FLT_END; - } - } - - /* Check the mux protocols, if any, for each server attached to - * the current proxy */ - for (newsrv = curproxy->srv; newsrv; newsrv = newsrv->next) { - int mode = conn_pr_mode_to_proto_mode(curproxy->mode); - const struct mux_proto_list *mux_ent; - - if (srv_is_quic(newsrv)) { - if (!newsrv->mux_proto) { - /* Force QUIC as mux-proto on server with quic addresses, similarly to bind on FE side. */ - newsrv->mux_proto = get_mux_proto(ist("quic")); - } - } - - if (!newsrv->mux_proto) - continue; - - /* it is possible that an incorrect mux was referenced - * due to the proxy's mode not being taken into account - * on first pass. Let's adjust it now. - */ - mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->token, PROTO_SIDE_BE, mode); - - if (!mux_ent || !isteq(mux_ent->token, newsrv->mux_proto->token)) { - ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for server '%s' at [%s:%d].\n", - proxy_type_str(curproxy), curproxy->id, - (int)newsrv->mux_proto->token.len, - newsrv->mux_proto->token.ptr, - newsrv->id, newsrv->conf.file, newsrv->conf.line); - cfgerr++; - } - else { - if ((mux_ent->mux->flags & MX_FL_FRAMED) && !srv_is_quic(newsrv)) { - ha_alert("%s '%s' : MUX protocol '%.*s' is incompatible with stream transport used by server '%s' at [%s:%d].\n", - proxy_type_str(curproxy), curproxy->id, - (int)newsrv->mux_proto->token.len, - newsrv->mux_proto->token.ptr, - newsrv->id, newsrv->conf.file, newsrv->conf.line); - cfgerr++; - } - else if (!(mux_ent->mux->flags & MX_FL_FRAMED) && srv_is_quic(newsrv)) { - ha_alert("%s '%s' : MUX protocol '%.*s' is incompatible with framed transport used by server '%s' at [%s:%d].\n", - proxy_type_str(curproxy), curproxy->id, - (int)newsrv->mux_proto->token.len, - newsrv->mux_proto->token.ptr, - newsrv->id, newsrv->conf.file, newsrv->conf.line); - cfgerr++; - } - } - - /* update the mux */ - newsrv->mux_proto = mux_ent; - } - - /* Allocate default tcp-check rules for proxies without - * explicit rules. - */ - if (curproxy->cap & PR_CAP_BE) { - if (!(curproxy->options2 & PR_O2_CHK_ANY)) { - struct tcpcheck_ruleset *rs = NULL; - struct tcpcheck_rules *rules = &curproxy->tcpcheck_rules; - - curproxy->options2 |= PR_O2_TCPCHK_CHK; - - rs = find_tcpcheck_ruleset("*tcp-check"); - if (!rs) { - rs = create_tcpcheck_ruleset("*tcp-check"); - if (rs == NULL) { - ha_alert("config: %s '%s': out of memory.\n", - proxy_type_str(curproxy), curproxy->id); - cfgerr++; - } - } - - free_tcpcheck_vars(&rules->preset_vars); - rules->list = &rs->rules; - rules->flags = 0; - } } } @@ -3953,7 +2674,6 @@ init_proxies_list_stage2: if (!LIST_ISEMPTY(&curpeers->peers_fe->conf.bind)) { struct list *l; struct bind_conf *bind_conf; - int ret; l = &curpeers->peers_fe->conf.bind; bind_conf = LIST_ELEM(l->n, typeof(bind_conf), by_fe); diff --git a/src/proxy.c b/src/proxy.c index d3b7c0f93..7f573659e 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -46,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -1591,6 +1599,1307 @@ int proxy_init_per_thr(struct proxy *px) return 0; } +int proxy_finalize(struct proxy *px, int *err_code) +{ + struct bind_conf *bind_conf; + struct server *newsrv; + struct switching_rule *rule; + struct server_rule *srule; + struct sticking_rule *mrule; + struct logger *tmplogger; + unsigned int next_id; + int cfgerr = 0; + char *err = NULL; + int i; + + /* check and reduce the bind-proc of each listener */ + list_for_each_entry(bind_conf, &px->conf.bind, by_fe) { + int mode = conn_pr_mode_to_proto_mode(px->mode); + const struct mux_proto_list *mux_ent; + int ret; + + /* Check the mux protocols, if any; before the check the ALPN */ + if (bind_conf->xprt && bind_conf->xprt == xprt_get(XPRT_QUIC)) { + if (!bind_conf->mux_proto) { + /* No protocol was specified. If we're using QUIC at the transport + * layer, we'll instantiate it as a mux as well. If QUIC is not + * compiled in, this will remain NULL. + */ + bind_conf->mux_proto = get_mux_proto(ist("quic")); + } + if (bind_conf->options & BC_O_ACC_PROXY) { + ha_alert("Binding [%s:%d] for %s %s: QUIC protocol does not support PROXY protocol yet." + " 'accept-proxy' option cannot be used with a QUIC listener.\n", + bind_conf->file, bind_conf->line, + proxy_type_str(px), px->id); + cfgerr++; + } + } + + if (bind_conf->mux_proto) { + /* it is possible that an incorrect mux was referenced + * due to the proxy's mode not being taken into account + * on first pass. Let's adjust it now. + */ + mux_ent = conn_get_best_mux_entry(bind_conf->mux_proto->token, PROTO_SIDE_FE, mode); + + if (!mux_ent || !isteq(mux_ent->token, bind_conf->mux_proto->token)) { + ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for 'bind %s' at [%s:%d].\n", + proxy_type_str(px), px->id, + (int)bind_conf->mux_proto->token.len, + bind_conf->mux_proto->token.ptr, + bind_conf->arg, bind_conf->file, bind_conf->line); + cfgerr++; + } + else { + if ((mux_ent->mux->flags & MX_FL_FRAMED) && !(bind_conf->options & BC_O_USE_SOCK_DGRAM)) { + ha_alert("%s '%s' : frame-based MUX protocol '%.*s' is incompatible with stream transport of 'bind %s' at [%s:%d].\n", + proxy_type_str(px), px->id, + (int)bind_conf->mux_proto->token.len, + bind_conf->mux_proto->token.ptr, + bind_conf->arg, bind_conf->file, bind_conf->line); + cfgerr++; + } + else if (!(mux_ent->mux->flags & MX_FL_FRAMED) && !(bind_conf->options & BC_O_USE_SOCK_STREAM)) { + ha_alert("%s '%s' : stream-based MUX protocol '%.*s' is incompatible with framed transport of 'bind %s' at [%s:%d].\n", + proxy_type_str(px), px->id, + (int)bind_conf->mux_proto->token.len, + bind_conf->mux_proto->token.ptr, + bind_conf->arg, bind_conf->file, bind_conf->line); + cfgerr++; + } + } + + /* update the mux */ + bind_conf->mux_proto = mux_ent; + } + + + /* HTTP frontends with "h2" as ALPN/NPN will work in + * HTTP/2 and absolutely require buffers 16kB or larger. + */ +#ifdef USE_OPENSSL + /* no-alpn ? If so, it's the right moment to remove it */ + if (bind_conf->ssl_conf.alpn_str && !bind_conf->ssl_conf.alpn_len) { + ha_free(&bind_conf->ssl_conf.alpn_str); + } +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + else if (!bind_conf->ssl_conf.alpn_str && !bind_conf->ssl_conf.npn_str && + ((bind_conf->options & BC_O_USE_SSL) || bind_conf->xprt == xprt_get(XPRT_QUIC)) && + px->mode == PR_MODE_HTTP && global.tune.bufsize >= 16384) { + + /* Neither ALPN nor NPN were explicitly set nor disabled, we're + * in HTTP mode with an SSL or QUIC listener, we can enable ALPN. + * Note that it's in binary form. First we try to set the ALPN from + * mux proto if set. Otherwise rely on the default ALPN. + */ + if (bind_conf->mux_proto && bind_conf->mux_proto->alpn) + bind_conf->ssl_conf.alpn_str = strdup(bind_conf->mux_proto->alpn); + else if (bind_conf->xprt == xprt_get(XPRT_QUIC)) + bind_conf->ssl_conf.alpn_str = strdup("\002h3"); + else + bind_conf->ssl_conf.alpn_str = strdup("\002h2\010http/1.1"); + + if (!bind_conf->ssl_conf.alpn_str) { + ha_alert("Proxy '%s': out of memory while trying to allocate a default alpn string in 'bind %s' at [%s:%d].\n", + px->id, bind_conf->arg, bind_conf->file, bind_conf->line); + cfgerr++; + *err_code |= ERR_FATAL | ERR_ALERT; + goto out; + } + bind_conf->ssl_conf.alpn_len = strlen(bind_conf->ssl_conf.alpn_str); + } +#endif /* TLSEXT_TYPE_application_layer_protocol_negotiation */ + + + if (px->mode == PR_MODE_HTTP && global.tune.bufsize < 16384) { +#ifdef OPENSSL_NPN_NEGOTIATED + /* check NPN */ + if (bind_conf->ssl_conf.npn_str && strstr(bind_conf->ssl_conf.npn_str, "\002h2")) { + ha_alert("HTTP frontend '%s' enables HTTP/2 via NPN at [%s:%d], so global.tune.bufsize must be at least 16384 bytes (%d now).\n", + px->id, bind_conf->file, bind_conf->line, global.tune.bufsize); + cfgerr++; + } +#endif /* OPENSSL_NPN_NEGOTIATED */ +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation + /* check ALPN */ + if (bind_conf->ssl_conf.alpn_str && strstr(bind_conf->ssl_conf.alpn_str, "\002h2")) { + ha_alert("HTTP frontend '%s' enables HTTP/2 via ALPN at [%s:%d], so global.tune.bufsize must be at least 16384 bytes (%d now).\n", + px->id, bind_conf->file, bind_conf->line, global.tune.bufsize); + cfgerr++; + } +#endif /* TLSEXT_TYPE_application_layer_protocol_negotiation */ + } /* HTTP && bufsize < 16384 */ +#endif /* USE_OPENSSL */ + +#ifdef USE_QUIC + if (bind_conf->xprt == xprt_get(XPRT_QUIC)) { + const struct quic_cc_algo *cc_algo = bind_conf->quic_cc_algo ? + bind_conf->quic_cc_algo : default_quic_cc_algo; + + if (!(cc_algo->flags & QUIC_CC_ALGO_FL_OPT_PACING) && + !(quic_tune.fe.fb_opts & QUIC_TUNE_FB_TX_PACING)) { + ha_warning("Binding [%s:%d] for %s %s: using the selected congestion algorithm without pacing may cause slowdowns or high loss rates during transfers.\n", + bind_conf->file, bind_conf->line, + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + } +#endif /* USE_QUIC */ + + /* finish the bind setup */ + ret = bind_complete_thread_setup(bind_conf, err_code); + if (ret != 0) { + cfgerr += ret; + if (*err_code & ERR_FATAL) + goto out; + } + + if (bind_generate_guid(bind_conf)) { + cfgerr++; + *err_code |= ERR_FATAL | ERR_ALERT; + goto out; + } + } + + switch (px->mode) { + case PR_MODE_TCP: + cfgerr += proxy_cfg_ensure_no_http(px); + cfgerr += proxy_cfg_ensure_no_log(px); + break; + + case PR_MODE_HTTP: + cfgerr += proxy_cfg_ensure_no_log(px); + px->http_needed = 1; + break; + + case PR_MODE_CLI: + cfgerr += proxy_cfg_ensure_no_http(px); + cfgerr += proxy_cfg_ensure_no_log(px); + break; + + case PR_MODE_SYSLOG: + /* this mode is initialized as the classic tcp proxy */ + cfgerr += proxy_cfg_ensure_no_http(px); + break; + + case PR_MODE_SPOP: + cfgerr += proxy_cfg_ensure_no_http(px); + cfgerr += proxy_cfg_ensure_no_log(px); + break; + + case PR_MODE_PEERS: + case PR_MODES: + /* should not happen, bug gcc warn missing switch statement */ + ha_alert("%s '%s' cannot initialize this proxy mode (peers) in this way. NOTE: PLEASE REPORT THIS TO DEVELOPERS AS YOU'RE NOT SUPPOSED TO BE ABLE TO CREATE A CONFIGURATION TRIGGERING THIS!\n", + proxy_type_str(px), px->id); + cfgerr++; + break; + } + + if (!(px->cap & PR_CAP_INT) && (px->cap & PR_CAP_FE) && LIST_ISEMPTY(&px->conf.listeners)) { + ha_warning("%s '%s' has no 'bind' directive. Please declare it as a backend if this was intended.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + if (px->cap & PR_CAP_BE) { + if (px->lbprm.algo & BE_LB_KIND) { + if (px->options & PR_O_TRANSP) { + ha_alert("%s '%s' cannot use both transparent and balance mode.\n", + proxy_type_str(px), px->id); + cfgerr++; + } + else if (px->options & PR_O_DISPATCH) { + ha_warning("dispatch address of %s '%s' will be ignored in balance mode.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + } + else if (!(px->options & (PR_O_TRANSP | PR_O_DISPATCH))) { + /* If no LB algo is set in a backend, and we're not in + * transparent mode, dispatch mode nor proxy mode, we + * want to use balance random by default. + */ + px->lbprm.algo &= ~BE_LB_ALGO; + px->lbprm.algo |= BE_LB_ALGO_RND; + } + } + + if (px->options & PR_O_DISPATCH) + px->options &= ~PR_O_TRANSP; + else if (px->options & PR_O_TRANSP) + px->options &= ~PR_O_DISPATCH; + + if ((px->tcpcheck_rules.flags & TCPCHK_RULES_UNUSED_HTTP_RS)) { + ha_warning("%s '%s' uses http-check rules without 'option httpchk', so the rules are ignored.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK && + (px->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) != TCPCHK_RULES_HTTP_CHK) { + if (px->options & PR_O_DISABLE404) { + ha_warning("'%s' will be ignored for %s '%s' (requires 'option httpchk').\n", + "disable-on-404", proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + px->options &= ~PR_O_DISABLE404; + } + if (px->options2 & PR_O2_CHK_SNDST) { + ha_warning("'%s' will be ignored for %s '%s' (requires 'option httpchk').\n", + "send-state", proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + px->options2 &= ~PR_O2_CHK_SNDST; + } + } + + if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) { + if (!global.external_check) { + ha_alert("Proxy '%s' : '%s' unable to find required 'global.external-check'.\n", + px->id, "option external-check"); + cfgerr++; + } + if (!px->check_command) { + ha_alert("Proxy '%s' : '%s' unable to find required 'external-check command'.\n", + px->id, "option external-check"); + cfgerr++; + } + if (!(global.tune.options & GTUNE_INSECURE_FORK)) { + ha_warning("Proxy '%s' : 'insecure-fork-wanted' not enabled in the global section, '%s' will likely fail.\n", + px->id, "option external-check"); + *err_code |= ERR_WARN; + } + } + + if (px->email_alert.flags & PR_EMAIL_ALERT_SET) { + if (!(px->email_alert.mailers.name && px->email_alert.from && px->email_alert.to)) { + ha_warning("'email-alert' will be ignored for %s '%s' (the presence any of " + "'email-alert from', 'email-alert level' 'email-alert mailers', " + "'email-alert myhostname', or 'email-alert to' " + "requires each of 'email-alert from', 'email-alert mailers' and 'email-alert to' " + "to be present).\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + free_email_alert(px); + } + if (!px->email_alert.myhostname) + px->email_alert.myhostname = strdup(hostname); + } + + if (px->check_command) { + int clear = 0; + if ((px->options2 & PR_O2_CHK_ANY) != PR_O2_EXT_CHK) { + ha_warning("'%s' will be ignored for %s '%s' (requires 'option external-check').\n", + "external-check command", proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + clear = 1; + } + if (px->check_command[0] != '/' && !px->check_path) { + ha_alert("Proxy '%s': '%s' does not have a leading '/' and 'external-check path' is not set.\n", + px->id, "external-check command"); + cfgerr++; + } + if (clear) { + ha_free(&px->check_command); + } + } + + if (px->check_path) { + if ((px->options2 & PR_O2_CHK_ANY) != PR_O2_EXT_CHK) { + ha_warning("'%s' will be ignored for %s '%s' (requires 'option external-check').\n", + "external-check path", proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + ha_free(&px->check_path); + } + } + + /* if a default backend was specified, let's find it */ + if (px->defbe.name) { + struct proxy *target; + + target = proxy_be_by_name(px->defbe.name); + if (!target) { + ha_alert("Proxy '%s': unable to find required default_backend: '%s'.\n", + px->id, px->defbe.name); + cfgerr++; + } else if (target == px) { + ha_alert("Proxy '%s': loop detected for default_backend: '%s'.\n", + px->id, px->defbe.name); + cfgerr++; + } else if (target->mode != px->mode && + !(px->mode == PR_MODE_TCP && target->mode == PR_MODE_HTTP)) { + + ha_alert("%s %s '%s' (%s:%d) tries to use incompatible %s %s '%s' (%s:%d) as its default backend (see 'mode').\n", + proxy_mode_str(px->mode), proxy_type_str(px), px->id, + px->conf.file, px->conf.line, + proxy_mode_str(target->mode), proxy_type_str(target), target->id, + target->conf.file, target->conf.line); + cfgerr++; + } else { + free(px->defbe.name); + px->defbe.be = target; + /* Emit a warning if this proxy also has some servers */ + if (px->srv) { + ha_warning("In proxy '%s', the 'default_backend' rule always has precedence over the servers, which will never be used.\n", + px->id); + *err_code |= ERR_WARN; + } + if (target->mode == PR_MODE_HTTP) { + /* at least one of the used backends will provoke an + * HTTP upgrade + */ + px->options |= PR_O_HTTP_UPG; + } + } + } + + /* find the target proxy for 'use_backend' rules */ + list_for_each_entry(rule, &px->switching_rules, list) { + struct proxy *target; + struct logformat_node *node; + char *pxname; + + /* Try to parse the string as a log format expression. If the result + * of the parsing is only one entry containing a simple string, then + * it's a standard string corresponding to a static rule, thus the + * parsing is cancelled and be.name is restored to be resolved. + */ + pxname = rule->be.name; + lf_expr_init(&rule->be.expr); + px->conf.args.ctx = ARGC_UBK; + px->conf.args.file = rule->file; + px->conf.args.line = rule->line; + err = NULL; + if (!parse_logformat_string(pxname, px, &rule->be.expr, 0, SMP_VAL_FE_HRQ_HDR, &err)) { + ha_alert("Parsing [%s:%d]: failed to parse use_backend rule '%s' : %s.\n", + rule->file, rule->line, pxname, err); + free(err); + cfgerr++; + continue; + } + node = LIST_NEXT(&rule->be.expr.nodes.list, struct logformat_node *, list); + + if (!lf_expr_isempty(&rule->be.expr)) { + if (node->type != LOG_FMT_TEXT || node->list.n != &rule->be.expr.nodes.list) { + rule->dynamic = 1; + free(pxname); + /* backend is not yet known so we cannot assume its type, + * thus we should consider that at least one of the used + * backends may provoke HTTP upgrade + */ + px->options |= PR_O_HTTP_UPG; + continue; + } + /* Only one element in the list, a simple string: free the expression and + * fall back to static rule + */ + lf_expr_deinit(&rule->be.expr); + } + + rule->dynamic = 0; + rule->be.name = pxname; + + target = proxy_be_by_name(rule->be.name); + if (!target) { + ha_alert("Proxy '%s': unable to find required use_backend: '%s'.\n", + px->id, rule->be.name); + cfgerr++; + } else if (target == px) { + ha_alert("Proxy '%s': loop detected for use_backend: '%s'.\n", + px->id, rule->be.name); + cfgerr++; + } else if (target->mode != px->mode && + !(px->mode == PR_MODE_TCP && target->mode == PR_MODE_HTTP)) { + + ha_alert("%s %s '%s' (%s:%d) tries to use incompatible %s %s '%s' (%s:%d) in a 'use_backend' rule (see 'mode').\n", + proxy_mode_str(px->mode), proxy_type_str(px), px->id, + px->conf.file, px->conf.line, + proxy_mode_str(target->mode), proxy_type_str(target), target->id, + target->conf.file, target->conf.line); + cfgerr++; + } else { + ha_free(&rule->be.name); + rule->be.backend = target; + if (target->mode == PR_MODE_HTTP) { + /* at least one of the used backends will provoke an + * HTTP upgrade + */ + px->options |= PR_O_HTTP_UPG; + } + } + *err_code |= warnif_tcp_http_cond(px, rule->cond); + } + + /* find the target server for 'use_server' rules */ + list_for_each_entry(srule, &px->server_rules, list) { + struct server *target; + struct logformat_node *node; + char *server_name; + + /* We try to parse the string as a log format expression. If the result of the parsing + * is only one entry containing a single string, then it's a standard string corresponding + * to a static rule, thus the parsing is cancelled and we fall back to setting srv.ptr. + */ + server_name = srule->srv.name; + lf_expr_init(&srule->expr); + px->conf.args.ctx = ARGC_USRV; + err = NULL; + if (!parse_logformat_string(server_name, px, &srule->expr, 0, SMP_VAL_FE_HRQ_HDR, &err)) { + ha_alert("Parsing [%s:%d]; use-server rule failed to parse log-format '%s' : %s.\n", + srule->file, srule->line, server_name, err); + free(err); + cfgerr++; + continue; + } + node = LIST_NEXT(&srule->expr.nodes.list, struct logformat_node *, list); + + if (!lf_expr_isempty(&srule->expr)) { + if (node->type != LOG_FMT_TEXT || node->list.n != &srule->expr.nodes.list) { + srule->dynamic = 1; + free(server_name); + continue; + } + /* Only one element in the list, a simple string: free the expression and + * fall back to static rule + */ + lf_expr_deinit(&srule->expr); + } + + srule->dynamic = 0; + srule->srv.name = server_name; + target = server_find_by_name(px, srule->srv.name); + *err_code |= warnif_tcp_http_cond(px, srule->cond); + + if (!target) { + ha_alert("%s '%s' : unable to find server '%s' referenced in a 'use-server' rule.\n", + proxy_type_str(px), px->id, srule->srv.name); + cfgerr++; + continue; + } + ha_free(&srule->srv.name); + srule->srv.ptr = target; + target->flags |= SRV_F_NON_PURGEABLE; + } + + /* find the target table for 'stick' rules */ + list_for_each_entry(mrule, &px->sticking_rules, list) { + px->be_req_ana |= AN_REQ_STICKING_RULES; + if (mrule->flags & STK_IS_STORE) + px->be_rsp_ana |= AN_RES_STORE_RULES; + + if (!resolve_stick_rule(px, mrule)) + cfgerr++; + + *err_code |= warnif_tcp_http_cond(px, mrule->cond); + } + + /* find the target table for 'store response' rules */ + list_for_each_entry(mrule, &px->storersp_rules, list) { + px->be_rsp_ana |= AN_RES_STORE_RULES; + + if (!resolve_stick_rule(px, mrule)) + cfgerr++; + } + + /* check validity for 'tcp-request' layer 4/5/6/7 rules */ + cfgerr += check_action_rules(&px->tcp_req.l4_rules, px, err_code); + cfgerr += check_action_rules(&px->tcp_req.l5_rules, px, err_code); + cfgerr += check_action_rules(&px->tcp_req.inspect_rules, px, err_code); + cfgerr += check_action_rules(&px->tcp_rep.inspect_rules, px, err_code); + cfgerr += check_action_rules(&px->http_req_rules, px, err_code); + cfgerr += check_action_rules(&px->http_res_rules, px, err_code); + cfgerr += check_action_rules(&px->http_after_res_rules, px, err_code); + + /* Warn is a switch-mode http is used on a TCP listener with servers but no backend */ + if (!px->defbe.name && LIST_ISEMPTY(&px->switching_rules) && px->srv) { + if ((px->options & PR_O_HTTP_UPG) && px->mode == PR_MODE_TCP) + ha_warning("Proxy '%s' : 'switch-mode http' configured for a %s %s with no backend. " + "Incoming connections upgraded to HTTP cannot be routed to TCP servers\n", + px->id, proxy_mode_str(px->mode), proxy_type_str(px)); + } + + if (px->table && px->table->peers.name) { + struct peers *curpeers; + + for (curpeers = cfg_peers; curpeers; curpeers = curpeers->next) { + if (strcmp(curpeers->id, px->table->peers.name) == 0) { + ha_free(&px->table->peers.name); + px->table->peers.p = curpeers; + break; + } + } + + if (!curpeers) { + ha_alert("Proxy '%s': unable to find sync peers '%s'.\n", + px->id, px->table->peers.name); + ha_free(&px->table->peers.name); + px->table->peers.p = NULL; + cfgerr++; + } + else if (curpeers->disabled) { + /* silently disable this peers section */ + px->table->peers.p = NULL; + } + else if (!curpeers->peers_fe) { + ha_alert("Proxy '%s': unable to find local peer '%s' in peers section '%s'.\n", + px->id, localpeer, curpeers->id); + px->table->peers.p = NULL; + cfgerr++; + } + } + + + if (px->email_alert.mailers.name) { + struct mailers *curmailers = mailers; + + for (curmailers = mailers; curmailers; curmailers = curmailers->next) { + if (strcmp(curmailers->id, px->email_alert.mailers.name) == 0) + break; + } + if (!curmailers) { + ha_alert("Proxy '%s': unable to find mailers '%s'.\n", + px->id, px->email_alert.mailers.name); + free_email_alert(px); + cfgerr++; + } + else { + err = NULL; + if (init_email_alert(curmailers, px, &err)) { + ha_alert("Proxy '%s': %s.\n", px->id, err); + free(err); + cfgerr++; + } + } + } + + if (px->uri_auth && !(px->uri_auth->flags & STAT_F_CONVDONE) && + !LIST_ISEMPTY(&px->uri_auth->http_req_rules) && + (px->uri_auth->userlist || px->uri_auth->auth_realm )) { + ha_alert("%s '%s': stats 'auth'/'realm' and 'http-request' can't be used at the same time.\n", + "proxy", px->id); + cfgerr++; + goto out_uri_auth_compat; + } + + if (px->uri_auth && px->uri_auth->userlist && + (!(px->uri_auth->flags & STAT_F_CONVDONE) || + LIST_ISEMPTY(&px->uri_auth->http_req_rules))) { + const char *uri_auth_compat_req[10]; + struct act_rule *rule; + i = 0; + + /* build the ACL condition from scratch. We're relying on anonymous ACLs for that */ + uri_auth_compat_req[i++] = "auth"; + + if (px->uri_auth->auth_realm) { + uri_auth_compat_req[i++] = "realm"; + uri_auth_compat_req[i++] = px->uri_auth->auth_realm; + } + + uri_auth_compat_req[i++] = "unless"; + uri_auth_compat_req[i++] = "{"; + uri_auth_compat_req[i++] = "http_auth(.internal-stats-userlist)"; + uri_auth_compat_req[i++] = "}"; + uri_auth_compat_req[i++] = ""; + + rule = parse_http_req_cond(uri_auth_compat_req, "internal-stats-auth-compat", 0, px); + if (!rule) { + cfgerr++; + goto out; + } + + LIST_APPEND(&px->uri_auth->http_req_rules, &rule->list); + + if (px->uri_auth->auth_realm) { + ha_free(&px->uri_auth->auth_realm); + } + px->uri_auth->flags |= STAT_F_CONVDONE; + } + out_uri_auth_compat: + + /* check whether we have a logger that uses RFC5424 log format */ + list_for_each_entry(tmplogger, &px->loggers, list) { + if (tmplogger->format == LOG_FORMAT_RFC5424) { + if (!px->logformat_sd.str) { + /* set the default logformat_sd_string */ + px->logformat_sd.str = default_rfc5424_sd_log_format; + } + break; + } + } + + /* compile the log format */ + if (!(px->cap & PR_CAP_FE)) { + lf_expr_deinit(&px->logformat); + lf_expr_deinit(&px->logformat_sd); + } + + if (px->logformat.str) { + px->conf.args.ctx = ARGC_LOG; + px->conf.args.file = px->logformat.conf.file; + px->conf.args.line = px->logformat.conf.line; + err = NULL; + if (!lf_expr_compile(&px->logformat, &px->conf.args, + LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES, + SMP_VAL_FE_LOG_END, &err) || + !lf_expr_postcheck(&px->logformat, px, &err)) { + ha_alert("Parsing [%s:%d]: failed to parse log-format : %s.\n", + px->logformat.conf.file, px->logformat.conf.line, err); + free(err); + cfgerr++; + } + px->conf.args.file = NULL; + px->conf.args.line = 0; + } + + if (px->logformat_sd.str) { + px->conf.args.ctx = ARGC_LOGSD; + px->conf.args.file = px->logformat_sd.conf.file; + px->conf.args.line = px->logformat_sd.conf.line; + err = NULL; + if (!lf_expr_compile(&px->logformat_sd, &px->conf.args, + LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES, + SMP_VAL_FE_LOG_END, &err) || + !add_to_logformat_list(NULL, NULL, LF_SEPARATOR, &px->logformat_sd, &err) || + !lf_expr_postcheck(&px->logformat_sd, px, &err)) { + ha_alert("Parsing [%s:%d]: failed to parse log-format-sd : %s.\n", + px->logformat_sd.conf.file, px->logformat_sd.conf.line, err); + free(err); + cfgerr++; + } + px->conf.args.file = NULL; + px->conf.args.line = 0; + } + + if (px->format_unique_id.str) { + int where = 0; + + px->conf.args.ctx = ARGC_UIF; + px->conf.args.file = px->format_unique_id.conf.file; + px->conf.args.line = px->format_unique_id.conf.line; + err = NULL; + if (px->cap & PR_CAP_FE) + where |= SMP_VAL_FE_HRQ_HDR; + if (px->cap & PR_CAP_BE) + where |= SMP_VAL_BE_HRQ_HDR; + if (!lf_expr_compile(&px->format_unique_id, &px->conf.args, + LOG_OPT_HTTP|LOG_OPT_MERGE_SPACES, where, &err) || + !lf_expr_postcheck(&px->format_unique_id, px, &err)) { + ha_alert("Parsing [%s:%d]: failed to parse unique-id : %s.\n", + px->format_unique_id.conf.file, px->format_unique_id.conf.line, err); + free(err); + cfgerr++; + } + px->conf.args.file = NULL; + px->conf.args.line = 0; + } + + if (px->logformat_error.str) { + px->conf.args.ctx = ARGC_LOG; + px->conf.args.file = px->logformat_error.conf.file; + px->conf.args.line = px->logformat_error.conf.line; + err = NULL; + if (!lf_expr_compile(&px->logformat_error, &px->conf.args, + LOG_OPT_MANDATORY|LOG_OPT_MERGE_SPACES, + SMP_VAL_FE_LOG_END, &err) || + !lf_expr_postcheck(&px->logformat_error, px, &err)) { + ha_alert("Parsing [%s:%d]: failed to parse error-log-format : %s.\n", + px->logformat_error.conf.file, px->logformat_error.conf.line, err); + free(err); + cfgerr++; + } + px->conf.args.file = NULL; + px->conf.args.line = 0; + } + + /* "balance hash" needs to compile its expression + * (log backends will handle this in proxy log postcheck) + */ + if (px->mode != PR_MODE_SYSLOG && + (px->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_SMP) { + int idx = 0; + const char *args[] = { + px->lbprm.arg_str, + NULL, + }; + + err = NULL; + px->conf.args.ctx = ARGC_USRV; // same context as use_server. + px->lbprm.expr = + sample_parse_expr((char **)args, &idx, + px->conf.file, px->conf.line, + &err, &px->conf.args, NULL); + + if (!px->lbprm.expr) { + ha_alert("%s '%s' [%s:%d]: failed to parse 'balance hash' expression '%s' in : %s.\n", + proxy_type_str(px), px->id, + px->conf.file, px->conf.line, + px->lbprm.arg_str, err); + ha_free(&err); + cfgerr++; + } + else if (!(px->lbprm.expr->fetch->val & SMP_VAL_BE_SET_SRV)) { + ha_alert("%s '%s' [%s:%d]: error detected while parsing 'balance hash' expression '%s' " + "which requires information from %s, which is not available here.\n", + proxy_type_str(px), px->id, + px->conf.file, px->conf.line, + px->lbprm.arg_str, sample_src_names(px->lbprm.expr->fetch->use)); + cfgerr++; + } + else if (px->mode == PR_MODE_HTTP && (px->lbprm.expr->fetch->use & SMP_USE_L6REQ)) { + ha_warning("%s '%s' [%s:%d]: L6 sample fetch <%s> will be ignored in 'balance hash' expression in HTTP mode.\n", + proxy_type_str(px), px->id, + px->conf.file, px->conf.line, + px->lbprm.arg_str); + } + else + px->http_needed |= !!(px->lbprm.expr->fetch->use & SMP_USE_HTTP_ANY); + } + + /* only now we can check if some args remain unresolved. + * This must be done after the users and groups resolution. + */ + err = NULL; + i = smp_resolve_args(px, &err); + cfgerr += i; + if (i) { + indent_msg(&err, 8); + ha_alert("%s%s\n", i > 1 ? "multiple argument resolution errors:" : "", err); + ha_free(&err); + } else + cfgerr += acl_find_targets(px); + + if (!(px->cap & PR_CAP_INT) && (px->mode == PR_MODE_TCP || px->mode == PR_MODE_HTTP) && + (((px->cap & PR_CAP_FE) && !px->timeout.client) || + ((px->cap & PR_CAP_BE) && (px->srv) && + (!px->timeout.connect || + (!px->timeout.server && (px->mode == PR_MODE_HTTP || !px->timeout.tunnel)))))) { + ha_warning("missing timeouts for %s '%s'.\n" + " | While not properly invalid, you will certainly encounter various problems\n" + " | with such a configuration. To fix this, please ensure that all following\n" + " | timeouts are set to a non-zero value: 'client', 'connect', 'server'.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + /* Historically, the tarpit and queue timeouts were inherited from contimeout. + * We must still support older configurations, so let's find out whether those + * parameters have been set or must be copied from contimeouts. + */ + if (!px->timeout.tarpit) + px->timeout.tarpit = px->timeout.connect; + if ((px->cap & PR_CAP_BE) && !px->timeout.queue) + px->timeout.queue = px->timeout.connect; + + if ((px->tcpcheck_rules.flags & TCPCHK_RULES_UNUSED_TCP_RS)) { + ha_warning("%s '%s' uses tcp-check rules without 'option tcp-check', so the rules are ignored.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + /* ensure that cookie capture length is not too large */ + if (px->capture_len >= global.tune.cookie_len) { + ha_warning("truncating capture length to %d bytes for %s '%s'.\n", + global.tune.cookie_len - 1, proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + px->capture_len = global.tune.cookie_len - 1; + } + + /* The small pools required for the capture lists */ + if (px->nb_req_cap) { + px->req_cap_pool = create_pool("ptrcap", + px->nb_req_cap * sizeof(char *), + MEM_F_SHARED); + } + + if (px->nb_rsp_cap) { + px->rsp_cap_pool = create_pool("ptrcap", + px->nb_rsp_cap * sizeof(char *), + MEM_F_SHARED); + } + + switch (px->load_server_state_from_file) { + case PR_SRV_STATE_FILE_UNSPEC: + px->load_server_state_from_file = PR_SRV_STATE_FILE_NONE; + break; + case PR_SRV_STATE_FILE_GLOBAL: + if (!global.server_state_file) { + ha_warning("backend '%s' configured to load server state file from global section 'server-state-file' directive. Unfortunately, 'server-state-file' is not set!\n", + px->id); + *err_code |= ERR_WARN; + } + break; + } + + /* first, we will invert the servers list order */ + newsrv = NULL; + while (px->srv) { + struct server *next; + + next = px->srv->next; + px->srv->next = newsrv; + newsrv = px->srv; + if (!next) + break; + px->srv = next; + } + + /* Check that no server name conflicts. This causes trouble in the stats. + * We only emit an error for the first conflict affecting each server, + * in order to avoid combinatory explosion if all servers have the same + * name. Since servers names are stored in a tree before landing here, + * we simply have to check for the current server's duplicates to spot + * conflicts. + */ + for (newsrv = px->srv; newsrv; newsrv = newsrv->next) { + struct server *other_srv; + + /* Note: internal servers are not always registered and + * they do not conflict. + */ + if (!ceb_intree(&newsrv->conf.name_node)) + continue; + + for (other_srv = newsrv; + (other_srv = cebis_item_prev_dup(&px->conf.used_server_name, conf.name_node, id, other_srv)); ) { + ha_alert("parsing [%s:%d] : %s '%s', another server named '%s' was already defined at line %d, please use distinct names.\n", + newsrv->conf.file, newsrv->conf.line, + proxy_type_str(px), px->id, + newsrv->id, other_srv->conf.line); + cfgerr++; + break; + } + } + + /* assign automatic UIDs to servers which don't have one yet */ + next_id = 1; + newsrv = px->srv; + while (newsrv != NULL) { + if (!newsrv->puid) { + /* server ID not set, use automatic numbering with first + * spare entry starting with next_svid. + */ + next_id = server_get_next_id(px, next_id); + newsrv->puid = next_id; + server_index_id(px, newsrv); + } + + next_id++; + newsrv = newsrv->next; + } + + px->lbprm.wmult = 1; /* default weight multiplier */ + px->lbprm.wdiv = 1; /* default weight divider */ + + /* + * If this server supports a maxconn parameter, it needs a dedicated + * tasks to fill the emptied slots when a connection leaves. + * Also, resolve deferred tracking dependency if needed. + */ + newsrv = px->srv; + while (newsrv != NULL) { + set_usermsgs_ctx(newsrv->conf.file, newsrv->conf.line, &newsrv->obj_type); + + srv_minmax_conn_apply(newsrv); + + /* this will also properly set the transport layer for + * prod and checks + * if default-server have use_ssl, prerare ssl init + * without activating it */ + if (newsrv->use_ssl == 1 || newsrv->check.use_ssl == 1 || + (newsrv->proxy->options & PR_O_TCPCHK_SSL) || + ((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) { + if (xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv) + cfgerr += xprt_get(XPRT_SSL)->prepare_srv(newsrv); + else if (xprt_get(XPRT_QUIC) && xprt_get(XPRT_QUIC)->prepare_srv) + cfgerr += xprt_get(XPRT_QUIC)->prepare_srv(newsrv); + } + + if (newsrv->use_ssl == 1 || ((newsrv->flags & SRV_F_DEFSRV_USE_SSL) && newsrv->use_ssl != 1)) { + /* In HTTP only, if the SNI is not set and we can rely on the host + * header value, fill the sni expression accordingly + */ + if (!newsrv->sni_expr && newsrv->proxy->mode == PR_MODE_HTTP && + !(newsrv->ssl_ctx.options & SRV_SSL_O_NO_AUTO_SNI)) { + newsrv->sni_expr = strdup("req.hdr(host),field(1,:)"); + + err = NULL; + if (server_parse_exprs(newsrv, px, &err)) { + ha_alert("parsing [%s:%d]: failed to parse auto SNI expression: %s\n", + newsrv->conf.file, newsrv->conf.line, err); + free(err); + ++cfgerr; + goto next_srv; + } + } + } + + + if ((newsrv->flags & SRV_F_FASTOPEN) && + ((px->retry_type & (PR_RE_DISCONNECTED | PR_RE_TIMEOUT)) != + (PR_RE_DISCONNECTED | PR_RE_TIMEOUT))) + ha_warning("server has tfo activated, the backend should be configured with at least 'conn-failure', 'empty-response' and 'response-timeout' or we wouldn't be able to retry the connection on failure.\n"); + + if (newsrv->trackit) { + if (srv_apply_track(newsrv, px)) { + ++cfgerr; + goto next_srv; + } + } + + next_srv: + reset_usermsgs_ctx(); + newsrv = newsrv->next; + } + + /* + * Try to generate dynamic cookies for servers now. + * It couldn't be done earlier, since at the time we parsed + * the server line, we may not have known yet that we + * should use dynamic cookies, or the secret key may not + * have been provided yet. + */ + if (px->ck_opts & PR_CK_DYNAMIC) { + newsrv = px->srv; + while (newsrv != NULL) { + srv_set_dyncookie(newsrv); + newsrv = newsrv->next; + } + + } + /* We have to initialize the server lookup mechanism depending + * on what LB algorithm was chosen. + */ + + px->lbprm.algo &= ~(BE_LB_LKUP | BE_LB_PROP_DYN); + switch (px->lbprm.algo & BE_LB_KIND) { + case BE_LB_KIND_RR: + if ((px->lbprm.algo & BE_LB_PARM) == BE_LB_RR_STATIC) { + px->lbprm.algo |= BE_LB_LKUP_MAP; + init_server_map(px); + } else if ((px->lbprm.algo & BE_LB_PARM) == BE_LB_RR_RANDOM) { + px->lbprm.algo |= BE_LB_LKUP_CHTREE | BE_LB_PROP_DYN; + if (chash_init_server_tree(px) < 0) { + cfgerr++; + } + } else { + px->lbprm.algo |= BE_LB_LKUP_RRTREE | BE_LB_PROP_DYN; + fwrr_init_server_groups(px); + } + break; + + case BE_LB_KIND_CB: + if ((px->lbprm.algo & BE_LB_PARM) == BE_LB_CB_LC) { + px->lbprm.algo |= BE_LB_LKUP_LCTREE | BE_LB_PROP_DYN; + fwlc_init_server_tree(px); + } else { + px->lbprm.algo |= BE_LB_LKUP_FSTREE | BE_LB_PROP_DYN; + fas_init_server_tree(px); + } + break; + + case BE_LB_KIND_HI: + if ((px->lbprm.algo & BE_LB_HASH_TYPE) == BE_LB_HASH_CONS) { + px->lbprm.algo |= BE_LB_LKUP_CHTREE | BE_LB_PROP_DYN; + if (chash_init_server_tree(px) < 0) { + cfgerr++; + } + } else { + px->lbprm.algo |= BE_LB_LKUP_MAP; + init_server_map(px); + } + break; + case BE_LB_KIND_SA: + if ((px->lbprm.algo & BE_LB_PARM) == BE_LB_SA_SS) { + px->lbprm.algo |= BE_LB_PROP_DYN; + init_server_ss(px); + } + break; + } + HA_RWLOCK_INIT(&px->lbprm.lock); + + if (px->options & PR_O_LOGASAP) + px->to_log &= ~LW_BYTES; + + if (!(px->cap & PR_CAP_INT) && (px->mode == PR_MODE_TCP || px->mode == PR_MODE_HTTP) && + (px->cap & PR_CAP_FE) && LIST_ISEMPTY(&px->loggers) && + (!lf_expr_isempty(&px->logformat) || !lf_expr_isempty(&px->logformat_sd))) { + ha_warning("log format ignored for %s '%s' since it has no log address.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + if (px->mode != PR_MODE_HTTP && !(px->options & PR_O_HTTP_UPG)) { + int optnum; + + if (px->uri_auth) { + ha_warning("'stats' statement ignored for %s '%s' as it requires HTTP mode.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + stats_uri_auth_drop(px->uri_auth); + px->uri_auth = NULL; + } + + if (px->capture_name) { + ha_warning("'capture' statement ignored for %s '%s' as it requires HTTP mode.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + if (isttest(px->monitor_uri)) { + ha_warning("'monitor-uri' statement ignored for %s '%s' as it requires HTTP mode.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + if (!LIST_ISEMPTY(&px->http_req_rules)) { + ha_warning("'http-request' rules ignored for %s '%s' as they require HTTP mode.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + if (!LIST_ISEMPTY(&px->http_res_rules)) { + ha_warning("'http-response' rules ignored for %s '%s' as they require HTTP mode.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + if (!LIST_ISEMPTY(&px->http_after_res_rules)) { + ha_warning("'http-after-response' rules ignored for %s '%s' as they require HTTP mode.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + if (!LIST_ISEMPTY(&px->redirect_rules)) { + ha_warning("'redirect' rules ignored for %s '%s' as they require HTTP mode.\n", + proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + } + + for (optnum = 0; cfg_opts[optnum].name; optnum++) { + if (cfg_opts[optnum].mode == PR_MODE_HTTP && + (px->cap & cfg_opts[optnum].cap) && + (px->options & cfg_opts[optnum].val)) { + ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", + cfg_opts[optnum].name, proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + px->options &= ~cfg_opts[optnum].val; + } + } + + for (optnum = 0; cfg_opts2[optnum].name; optnum++) { + if (cfg_opts2[optnum].mode == PR_MODE_HTTP && + (px->cap & cfg_opts2[optnum].cap) && + (px->options2 & cfg_opts2[optnum].val)) { + ha_warning("'option %s' ignored for %s '%s' as it requires HTTP mode.\n", + cfg_opts2[optnum].name, proxy_type_str(px), px->id); + *err_code |= ERR_WARN; + px->options2 &= ~cfg_opts2[optnum].val; + } + } + +#if defined(CONFIG_HAP_TRANSPARENT) + if (px->conn_src.bind_hdr_occ) { + px->conn_src.bind_hdr_occ = 0; + ha_warning("%s '%s' : ignoring use of header %s as source IP in non-HTTP mode.\n", + proxy_type_str(px), px->id, px->conn_src.bind_hdr_name); + *err_code |= ERR_WARN; + } +#endif /* CONFIG_HAP_TRANSPARENT */ + } + + /* + * ensure that we're not cross-dressing a TCP server into HTTP. + */ + newsrv = px->srv; + while (newsrv != NULL) { + if ((px->mode != PR_MODE_HTTP) && newsrv->rdr_len) { + ha_alert("%s '%s' : server cannot have cookie or redirect prefix in non-HTTP mode.\n", + proxy_type_str(px), px->id); + cfgerr++; + } + + if ((px->mode != PR_MODE_HTTP) && newsrv->cklen) { + ha_warning("%s '%s' : ignoring cookie for server '%s' as HTTP mode is disabled.\n", + proxy_type_str(px), px->id, newsrv->id); + *err_code |= ERR_WARN; + } + + if ((newsrv->flags & SRV_F_MAPPORTS) && (px->options2 & PR_O2_RDPC_PRST)) { + ha_warning("%s '%s' : RDP cookie persistence will not work for server '%s' because it lacks an explicit port number.\n", + proxy_type_str(px), px->id, newsrv->id); + *err_code |= ERR_WARN; + } + +#if defined(CONFIG_HAP_TRANSPARENT) + if (px->mode != PR_MODE_HTTP && newsrv->conn_src.bind_hdr_occ) { + newsrv->conn_src.bind_hdr_occ = 0; + ha_warning("%s '%s' : server %s cannot use header %s as source IP in non-HTTP mode.\n", + proxy_type_str(px), px->id, newsrv->id, newsrv->conn_src.bind_hdr_name); + *err_code |= ERR_WARN; + } +#endif /* CONFIG_HAP_TRANSPARENT */ + + if ((px->mode != PR_MODE_HTTP) && (px->options & PR_O_REUSE_MASK) != PR_O_REUSE_NEVR) + px->options &= ~PR_O_REUSE_MASK; + if (px->mode == PR_MODE_SPOP) + px->options |= PR_O_REUSE_ALWS; + + if ((px->mode != PR_MODE_HTTP) && newsrv->flags & SRV_F_RHTTP) { + ha_alert("%s '%s' : server %s uses reverse HTTP addressing which can only be used with HTTP mode.\n", + proxy_type_str(px), px->id, newsrv->id); + cfgerr++; + *err_code |= ERR_FATAL | ERR_ALERT; + goto out; + } + + newsrv = newsrv->next; + } + + /* Check filter configuration, if any */ + cfgerr += flt_check(px); + + if (px->cap & PR_CAP_FE) { + if (!px->accept) + px->accept = frontend_accept; + + if (!LIST_ISEMPTY(&px->tcp_req.inspect_rules) || + (px->defpx && !LIST_ISEMPTY(&px->defpx->tcp_req.inspect_rules))) + px->fe_req_ana |= AN_REQ_INSPECT_FE; + + if (px->mode == PR_MODE_HTTP) { + px->fe_req_ana |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_PROCESS_FE; + px->fe_rsp_ana |= AN_RES_WAIT_HTTP | AN_RES_HTTP_PROCESS_FE; + } + + if (px->mode == PR_MODE_CLI) { + px->fe_req_ana |= AN_REQ_WAIT_CLI; + px->fe_rsp_ana |= AN_RES_WAIT_CLI; + } + + /* both TCP and HTTP must check switching rules */ + px->fe_req_ana |= AN_REQ_SWITCHING_RULES; + + /* Add filters analyzers if needed */ + if (!LIST_ISEMPTY(&px->filter_configs)) { + px->fe_req_ana |= AN_REQ_FLT_START_FE | AN_REQ_FLT_XFER_DATA | AN_REQ_FLT_END; + px->fe_rsp_ana |= AN_RES_FLT_START_FE | AN_RES_FLT_XFER_DATA | AN_RES_FLT_END; + } + } + + if (px->cap & PR_CAP_BE) { + if (!LIST_ISEMPTY(&px->tcp_req.inspect_rules) || + (px->defpx && !LIST_ISEMPTY(&px->defpx->tcp_req.inspect_rules))) + px->be_req_ana |= AN_REQ_INSPECT_BE; + + if (!LIST_ISEMPTY(&px->tcp_rep.inspect_rules) || + (px->defpx && !LIST_ISEMPTY(&px->defpx->tcp_rep.inspect_rules))) + px->be_rsp_ana |= AN_RES_INSPECT; + + if (px->mode == PR_MODE_HTTP) { + px->be_req_ana |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_INNER | AN_REQ_HTTP_PROCESS_BE; + px->be_rsp_ana |= AN_RES_WAIT_HTTP | AN_RES_HTTP_PROCESS_BE; + } + + /* If the backend does requires RDP cookie persistence, we have to + * enable the corresponding analyser. + */ + if (px->options2 & PR_O2_RDPC_PRST) + px->be_req_ana |= AN_REQ_PRST_RDP_COOKIE; + + /* Add filters analyzers if needed */ + if (!LIST_ISEMPTY(&px->filter_configs)) { + px->be_req_ana |= AN_REQ_FLT_START_BE | AN_REQ_FLT_XFER_DATA | AN_REQ_FLT_END; + px->be_rsp_ana |= AN_RES_FLT_START_BE | AN_RES_FLT_XFER_DATA | AN_RES_FLT_END; + } + } + + /* Check the mux protocols, if any, for each server attached to + * the current proxy */ + for (newsrv = px->srv; newsrv; newsrv = newsrv->next) { + int mode = conn_pr_mode_to_proto_mode(px->mode); + const struct mux_proto_list *mux_ent; + + if (srv_is_quic(newsrv)) { + if (!newsrv->mux_proto) { + /* Force QUIC as mux-proto on server with quic addresses, similarly to bind on FE side. */ + newsrv->mux_proto = get_mux_proto(ist("quic")); + } + } + + if (!newsrv->mux_proto) + continue; + + /* it is possible that an incorrect mux was referenced + * due to the proxy's mode not being taken into account + * on first pass. Let's adjust it now. + */ + mux_ent = conn_get_best_mux_entry(newsrv->mux_proto->token, PROTO_SIDE_BE, mode); + + if (!mux_ent || !isteq(mux_ent->token, newsrv->mux_proto->token)) { + ha_alert("%s '%s' : MUX protocol '%.*s' is not usable for server '%s' at [%s:%d].\n", + proxy_type_str(px), px->id, + (int)newsrv->mux_proto->token.len, + newsrv->mux_proto->token.ptr, + newsrv->id, newsrv->conf.file, newsrv->conf.line); + cfgerr++; + } + else { + if ((mux_ent->mux->flags & MX_FL_FRAMED) && !srv_is_quic(newsrv)) { + ha_alert("%s '%s' : MUX protocol '%.*s' is incompatible with stream transport used by server '%s' at [%s:%d].\n", + proxy_type_str(px), px->id, + (int)newsrv->mux_proto->token.len, + newsrv->mux_proto->token.ptr, + newsrv->id, newsrv->conf.file, newsrv->conf.line); + cfgerr++; + } + else if (!(mux_ent->mux->flags & MX_FL_FRAMED) && srv_is_quic(newsrv)) { + ha_alert("%s '%s' : MUX protocol '%.*s' is incompatible with framed transport used by server '%s' at [%s:%d].\n", + proxy_type_str(px), px->id, + (int)newsrv->mux_proto->token.len, + newsrv->mux_proto->token.ptr, + newsrv->id, newsrv->conf.file, newsrv->conf.line); + cfgerr++; + } + } + + /* update the mux */ + newsrv->mux_proto = mux_ent; + } + + /* Allocate default tcp-check rules for proxies without + * explicit rules. + */ + if (px->cap & PR_CAP_BE) { + if (!(px->options2 & PR_O2_CHK_ANY)) { + struct tcpcheck_ruleset *rs = NULL; + struct tcpcheck_rules *rules = &px->tcpcheck_rules; + + px->options2 |= PR_O2_TCPCHK_CHK; + + rs = find_tcpcheck_ruleset("*tcp-check"); + if (!rs) { + rs = create_tcpcheck_ruleset("*tcp-check"); + if (rs == NULL) { + ha_alert("config: %s '%s': out of memory.\n", + proxy_type_str(px), px->id); + cfgerr++; + } + } + + free_tcpcheck_vars(&rules->preset_vars); + rules->list = &rs->rules; + rules->flags = 0; + } + } + + out: + if (cfgerr) + *err_code |= ERR_ALERT | ERR_FATAL; + + return cfgerr; +} + /* Frees all dynamic settings allocated on a default proxy that's about to be * destroyed. Note that most of the fields are not even reset, so extreme care * is required here. -- 2.47.3