*/
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;
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.
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;
- }
}
}
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);
#include <haproxy/errors.h>
#include <haproxy/fd.h>
#include <haproxy/filters.h>
+#include <haproxy/frontend.h>
#include <haproxy/global.h>
#include <haproxy/guid.h>
#include <haproxy/http_ana.h>
#include <haproxy/http_rules.h>
#include <haproxy/mailers.h>
#include <haproxy/listener.h>
+#include <haproxy/lb_chash.h>
+#include <haproxy/lb_fas.h>
+#include <haproxy/lb_fwlc.h>
+#include <haproxy/lb_fwrr.h>
+#include <haproxy/lb_map.h>
+#include <haproxy/lb_ss.h>
#include <haproxy/log.h>
#include <haproxy/obj_type-t.h>
#include <haproxy/peers.h>
#include <haproxy/proxy.h>
#include <haproxy/sc_strm.h>
#include <haproxy/quic_tp.h>
+#include <haproxy/quic_tune.h>
#include <haproxy/server-t.h>
#include <haproxy/signal.h>
#include <haproxy/stats-t.h>
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.