2006-06-25 20:48:02 -04:00
/*
* Configuration parser
*
2011-01-06 11:51:27 -05:00
* Copyright 2000 - 2011 Willy Tarreau < w @ 1 wt . eu >
2006-06-25 20:48:02 -04:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
*/
2022-08-07 10:55:07 -04:00
/* This is to have crypt() and sched_setaffinity() defined on Linux */
2014-08-29 14:20:02 -04:00
# define _GNU_SOURCE
2022-08-07 10:55:07 -04:00
# ifdef USE_LIBCRYPT
2019-05-22 13:24:06 -04:00
# ifdef USE_CRYPT_H
2014-08-29 14:20:02 -04:00
/* some platforms such as Solaris need this */
# include <crypt.h>
# endif
2019-05-22 13:24:06 -04:00
# endif /* USE_LIBCRYPT */
2014-08-29 14:20:02 -04:00
2021-03-26 13:20:47 -04:00
# include <dirent.h>
2006-06-25 20:48:02 -04:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <netdb.h>
# include <ctype.h>
2007-03-25 09:39:23 -04:00
# include <pwd.h>
# include <grp.h>
2007-03-25 18:18:40 -04:00
# include <errno.h>
2022-08-07 10:55:07 -04:00
# ifdef USE_CPU_AFFINITY
# include <sched.h>
# endif
2007-06-10 18:29:26 -04:00
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
2006-06-25 20:48:02 -04:00
2025-07-07 09:33:40 -04:00
# include <import/cebis_tree.h>
2020-06-04 13:11:43 -04:00
# include <haproxy/acl.h>
2021-03-25 12:19:04 -04:00
# include <haproxy/action.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/api.h>
2021-05-06 09:49:04 -04:00
# include <haproxy/arg.h>
2020-06-04 04:36:03 -04:00
# include <haproxy/auth.h>
2020-06-04 16:50:02 -04:00
# include <haproxy/backend.h>
2020-06-04 05:18:28 -04:00
# include <haproxy/capture.h>
2021-07-16 09:39:28 -04:00
# include <haproxy/cfgcond.h>
2020-06-04 18:00:29 -04:00
# include <haproxy/cfgparse.h>
2020-06-04 15:07:02 -04:00
# include <haproxy/channel.h>
2020-06-04 12:21:56 -04:00
# include <haproxy/check.h>
2020-06-02 04:22:45 -04:00
# include <haproxy/chunk.h>
2021-10-08 03:33:24 -04:00
# include <haproxy/clock.h>
MEDIUM: counters: manage shared counters using dedicated helpers
proxies, listeners and server shared counters are now managed via helpers
added in one of the previous commits.
When guid is not set (ie: when not yet assigned), shared counters pointer
is allocated using calloc() (local memory) and a flag is set on the shared
counters struct to know how to manipulate (and free it). Else if guid is
set, then it means that the counters may be shared so while for now we
don't actually use a shared memory location the API is ready for that.
The way it works, for proxies and servers (for which guid is not known
during creation), we first call counters_{fe,be}_shared_get with guid not
set, which results in local pointer being retrieved (as if we just
manually called calloc() to retrieve a pointer). Later (during postparsing)
if guid is set we try to upgrade the pointer from local to shared.
Lastly, since the memory location for some objects (proxies and servers
counters) may change from creation to postparsing, let's update
counters->last_change member directly under counters_{fe,be}_shared_get()
so we don't miss it.
No change of behavior is expected, this is only preparation work.
2025-05-07 17:42:04 -04:00
# include <haproxy/counters.h>
2021-04-23 10:58:08 -04:00
# ifdef USE_CPU_AFFINITY
2021-04-14 10:16:03 -04:00
# include <haproxy/cpuset.h>
2025-01-22 11:17:59 -05:00
# include <haproxy/cpu_topo.h>
2021-04-23 10:58:08 -04:00
# endif
2020-06-04 12:02:10 -04:00
# include <haproxy/connection.h>
2020-05-27 10:10:29 -04:00
# include <haproxy/errors.h>
2020-06-04 15:29:29 -04:00
# include <haproxy/filters.h>
2020-06-04 05:23:07 -04:00
# include <haproxy/frontend.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/global.h>
2020-06-04 15:21:03 -04:00
# include <haproxy/http_ana.h>
2020-06-04 05:40:28 -04:00
# include <haproxy/http_rules.h>
2026-03-18 11:22:11 -04:00
# include <haproxy/http_htx.h>
2020-06-04 08:34:27 -04:00
# include <haproxy/lb_chash.h>
2020-06-04 08:37:38 -04:00
# include <haproxy/lb_fas.h>
2020-06-04 08:41:04 -04:00
# include <haproxy/lb_fwlc.h>
2020-06-04 08:45:03 -04:00
# include <haproxy/lb_fwrr.h>
2020-06-04 14:22:59 -04:00
# include <haproxy/lb_map.h>
2024-03-28 12:24:53 -04:00
# include <haproxy/lb_ss.h>
2020-06-04 08:58:24 -04:00
# include <haproxy/listener.h>
2020-06-04 16:01:04 -04:00
# include <haproxy/log.h>
2022-09-13 10:16:30 -04:00
# include <haproxy/sink.h>
2020-06-05 05:40:38 -04:00
# include <haproxy/mailers.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/namespace.h>
2025-01-30 08:50:19 -05:00
# include <haproxy/quic_cc-t.h>
2022-01-25 11:48:47 -05:00
# include <haproxy/quic_sock.h>
2025-01-30 12:01:53 -05:00
# include <haproxy/quic_tune.h>
2020-06-04 05:29:21 -04:00
# include <haproxy/obj_type-t.h>
2023-04-19 04:41:55 -04:00
# include <haproxy/openssl-compat.h>
2020-06-04 12:38:21 -04:00
# include <haproxy/peers-t.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/peers.h>
2020-06-02 03:38:52 -04:00
# include <haproxy/pool.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/protocol.h>
2020-06-04 16:29:18 -04:00
# include <haproxy/proxy.h>
2021-02-12 13:42:55 -05:00
# include <haproxy/resolvers.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/sample.h>
# include <haproxy/server.h>
2020-06-04 12:58:52 -04:00
# include <haproxy/session.h>
2020-06-04 12:46:44 -04:00
# include <haproxy/stick_table.h>
2020-06-04 17:46:14 -04:00
# include <haproxy/stream.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/task.h>
2020-06-04 11:42:48 -04:00
# include <haproxy/tcp_rules.h>
2021-07-23 09:46:46 -04:00
# include <haproxy/tcpcheck.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/thread.h>
# include <haproxy/tools.h>
2024-11-13 13:54:32 -05:00
# include <haproxy/uri_auth.h>
2006-06-25 20:48:02 -04:00
2014-03-18 08:54:18 -04:00
/* Used to chain configuration sections definitions. This list
* stores struct cfg_section
*/
struct list sections = LIST_HEAD_INIT ( sections ) ;
2017-10-23 08:36:34 -04:00
struct list postparsers = LIST_HEAD_INIT ( postparsers ) ;
2021-03-16 10:12:17 -04:00
extern struct proxy * mworker_proxy ;
2023-07-27 11:09:14 -04:00
/* curproxy is only valid during parsing and will be NULL afterwards. */
2023-08-01 05:18:00 -04:00
struct proxy * curproxy = NULL ;
MEDIUM: cfgparse: do not store unnamed defaults in name tree
Defaults section are indexed by their name in defproxy_by_name tree. For
named sections, there is no duplicate : if two instances have the same
name, the older one is removed from the tree. However, this was not the
case for unnamed defaults which are all stored inconditionnally in
defproxy_by_name.
This commit introduces a new approach for unnamed defaults. Now, these
instances are never inserted in the defproxy_by_name tree. Indeed, this
is not needed as no tree lookup is performed with empty names. This may
optimize slightly config parsing with a huge number of named and unnamed
defaults sections, as the first ones won't fill up the tree needlessly.
However, defproxy_by_name tree is also used to purge unreferenced
defaults instances, both on postparsing and deinit. Thus, a new approach
is needed for unnamed sections cleanup. Now, each time a new defaults is
parsed, if the previous instance is unnamed, it is freed unless if
referenced by a proxy. When config parsing is ended, a similar operation
is performed to ensure the last unnamed defaults section won't stay in
memory. To implement this, last_defproxy static variable is now set to
global. Unnamed sections which cannot be removed due to proxies
referencing proxies will still be removed when such proxies are freed
themselves, at runtime or on deinit.
2026-01-21 04:22:23 -05:00
/* last defaults section parsed, NULL after parsing */
struct proxy * last_defproxy = NULL ;
2023-07-27 11:09:14 -04:00
2018-11-11 09:40:36 -05:00
char * cursection = NULL ;
MEDIUM: config: don't enforce a low frontend maxconn value anymore
Historically the default frontend's maxconn used to be quite low (2000),
which was sufficient two decades ago but often proved to be a problem
when users had purposely set the global maxconn value but forgot to set
the frontend's.
There is no point in keeping this arbitrary limit for frontends : when
the global maxconn is lower, it's already too high and when the global
maxconn is much higher, it becomes a limiting factor which causes trouble
in production.
This commit allows the value to be set to zero, which becomes the new
default value, to mean it's not directly limited, or in fact it's set
to the global maxconn. Since this operation used to be performed before
computing a possibly automatic global maxconn based on memory limits,
the calculation of the maxconn value and its propagation to the backends'
fullconn has now moved to a dedicated function, proxy_adjust_all_maxconn(),
which is called once the global maxconn is stabilized.
This comes with two benefits :
1) a configuration missing "maxconn" in the defaults section will not
limit itself to a magically hardcoded value but will scale up to the
global maxconn ;
2) when the global maxconn is not set and memory limits are used instead,
the frontends' maxconn automatically adapts, and the backends' fullconn
as well.
2019-02-27 11:25:52 -05:00
int cfg_maxpconn = 0 ; /* # of simultaneous connections per proxy (-N) */
2009-03-15 10:23:16 -04:00
int cfg_maxconn = 0 ; /* # of simultaneous connections, (-n) */
2016-11-04 17:36:15 -04:00
char * cfg_scope = NULL ; /* the current scope during the configuration parsing */
2022-11-18 09:46:06 -05:00
int non_global_section_parsed = 0 ;
2006-06-25 20:48:02 -04:00
2021-04-27 14:29:11 -04:00
/* how to handle default paths */
static enum default_path_mode {
DEFAULT_PATH_CURRENT = 0 , /* "current": paths are relative to CWD (this is the default) */
DEFAULT_PATH_CONFIG , /* "config": paths are relative to config file */
DEFAULT_PATH_PARENT , /* "parent": paths are relative to config file's ".." */
DEFAULT_PATH_ORIGIN , /* "origin": paths are relative to default_path_origin */
} default_path_mode ;
2025-03-04 05:04:01 -05:00
char initial_cwd [ PATH_MAX ] ;
2021-04-27 14:29:11 -04:00
static char current_cwd [ PATH_MAX ] ;
[MEDIUM] add support for configuration keyword registration
Any module which needs configuration keywords may now dynamically
register a keyword in a given section, and associate it with a
configuration parsing function using cfg_register_keywords() from
a constructor function. This makes the configuration parser more
modular because it is not required anymore to touch cfg_parse.c.
Example :
static int parse_global_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in global section\n");
return 0;
}
static int parse_listen_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in listen section\n");
if (*args[1]) {
snprintf(err, errlen, "missing arg for listen_blah!!!");
return -1;
}
return 0;
}
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "blah", parse_global_blah },
{ CFG_LISTEN, "blah", parse_listen_blah },
{ 0, NULL, NULL },
}};
__attribute__((constructor))
static void __module_init(void)
{
cfg_register_keywords(&cfg_kws);
}
2008-07-09 13:39:06 -04:00
/* List head of all known configuration keywords */
2018-11-11 09:19:52 -05:00
struct cfg_kw_list cfg_keywords = {
[MEDIUM] add support for configuration keyword registration
Any module which needs configuration keywords may now dynamically
register a keyword in a given section, and associate it with a
configuration parsing function using cfg_register_keywords() from
a constructor function. This makes the configuration parser more
modular because it is not required anymore to touch cfg_parse.c.
Example :
static int parse_global_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in global section\n");
return 0;
}
static int parse_listen_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in listen section\n");
if (*args[1]) {
snprintf(err, errlen, "missing arg for listen_blah!!!");
return -1;
}
return 0;
}
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "blah", parse_global_blah },
{ CFG_LISTEN, "blah", parse_listen_blah },
{ 0, NULL, NULL },
}};
__attribute__((constructor))
static void __module_init(void)
{
cfg_register_keywords(&cfg_kws);
}
2008-07-09 13:39:06 -04:00
. list = LIST_HEAD_INIT ( cfg_keywords . list )
} ;
2026-01-15 10:41:38 -05:00
/*
* Shifts < args > one position to the left .
* This function tricky preserves internal allocated structure of the
* < args > . We defer the deallocation of the " shifted off " element , by
* making it an empty string and moving it into the gap that appears after
* the shift .
*/
static void
lshift_args ( char * * args )
{
int i ;
char * shifted ;
shifted = args [ 0 ] ;
for ( i = 0 ; * args [ i + 1 ] ; i + + )
args [ i ] = args [ i + 1 ] ;
* shifted = ' \0 ' ;
args [ i ] = shifted ;
}
2006-06-25 20:48:02 -04:00
/*
* converts < str > to a list of listeners which are dynamically allocated .
* The format is " {addr|'*'}:port[-end][,{addr|'*'}:port[-end]]* " , where :
* - < addr > can be empty or " * " to indicate INADDR_ANY ;
* - < port > is a numerical port from 1 to 65535 ;
* - < end > indicates to use the range from < port > to < end > instead ( inclusive ) .
* This can be repeated as many times as necessary , separated by a coma .
2012-09-20 14:01:39 -04:00
* Function returns 1 for success or 0 if error . In case of errors , if < err > is
* not NULL , it must be a valid pointer to either NULL or a freeable area that
* will be replaced with an error message .
2006-06-25 20:48:02 -04:00
*/
2012-09-20 14:01:39 -04:00
int str2listener ( char * str , struct proxy * curproxy , struct bind_conf * bind_conf , const char * file , int line , char * * err )
2006-06-25 20:48:02 -04:00
{
2020-09-16 11:58:55 -04:00
struct protocol * proto ;
2011-03-04 09:43:13 -05:00
char * next , * dupstr ;
2006-06-25 20:48:02 -04:00
int port , end ;
next = dupstr = strdup ( str ) ;
2009-10-04 09:43:17 -04:00
2006-06-25 20:48:02 -04:00
while ( next & & * next ) {
2017-09-15 02:10:44 -04:00
struct sockaddr_storage * ss2 ;
2013-03-10 18:51:38 -04:00
int fd = - 1 ;
2006-06-25 20:48:02 -04:00
str = next ;
/* 1) look for the end of the first address */
2009-01-27 10:57:08 -05:00
if ( ( next = strchr ( str , ' , ' ) ) ! = NULL ) {
2006-06-25 20:48:02 -04:00
* next + + = 0 ;
}
2023-11-09 05:19:24 -05:00
ss2 = str2sa_range ( str , NULL , & port , & end , & fd , & proto , NULL , err ,
2021-03-16 10:12:17 -04:00
( curproxy = = global . cli_fe | | curproxy = = mworker_proxy ) ? NULL : global . unix_bind . prefix ,
2024-08-26 05:50:24 -04:00
NULL , NULL , PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_PORT_RANGE |
2020-09-16 10:28:08 -04:00
PA_O_SOCKET_FD | PA_O_STREAM | PA_O_XPRT ) ;
2013-03-06 09:45:03 -05:00
if ( ! ss2 )
goto fail ;
2006-06-25 20:48:02 -04:00
2023-11-21 05:10:34 -05:00
if ( ss2 - > ss_family = = AF_CUST_RHTTP_SRV ) {
2023-10-19 06:05:31 -04:00
/* Check if a previous non reverse HTTP present is
* already defined . If DGRAM or STREAM is set , this
* indicates that we are currently parsing the second
* or more address .
*/
if ( bind_conf - > options & ( BC_O_USE_SOCK_DGRAM | BC_O_USE_SOCK_STREAM ) & &
! ( bind_conf - > options & BC_O_REVERSE_HTTP ) ) {
memprintf ( err , " Cannot mix reverse HTTP bind with others. \n " ) ;
goto fail ;
}
2023-11-21 05:10:34 -05:00
bind_conf - > rhttp_srvname = strdup ( str + strlen ( " rhttp@ " ) ) ;
if ( ! bind_conf - > rhttp_srvname ) {
2023-10-19 06:05:31 -04:00
memprintf ( err , " Cannot allocate reverse HTTP bind. \n " ) ;
goto fail ;
}
bind_conf - > options | = BC_O_REVERSE_HTTP ;
}
else if ( bind_conf - > options & BC_O_REVERSE_HTTP ) {
/* Standard address mixed with a previous reverse HTTP one. */
memprintf ( err , " Cannot mix reverse HTTP bind with others. \n " ) ;
goto fail ;
}
2013-03-06 09:45:03 -05:00
/* OK the address looks correct */
2022-05-20 10:15:01 -04:00
if ( proto - > proto_type = = PROTO_TYPE_DGRAM )
bind_conf - > options | = BC_O_USE_SOCK_DGRAM ;
else
bind_conf - > options | = BC_O_USE_SOCK_STREAM ;
if ( proto - > xprt_type = = PROTO_TYPE_DGRAM )
bind_conf - > options | = BC_O_USE_XPRT_DGRAM ;
else
bind_conf - > options | = BC_O_USE_XPRT_STREAM ;
2020-11-23 08:23:21 -05:00
2020-09-16 11:58:55 -04:00
if ( ! create_listeners ( bind_conf , ss2 , port , end , fd , proto , err ) ) {
2017-09-15 02:10:44 -04:00
memprintf ( err , " %s for address '%s'. \n " , * err , str ) ;
goto fail ;
}
2006-06-25 20:48:02 -04:00
} /* end while(next) */
free ( dupstr ) ;
2009-10-04 09:43:17 -04:00
return 1 ;
2006-06-25 20:48:02 -04:00
fail :
free ( dupstr ) ;
2009-10-04 09:43:17 -04:00
return 0 ;
2006-06-25 20:48:02 -04:00
}
2020-09-16 09:13:04 -04:00
/*
* converts < str > to a list of datagram - oriented listeners which are dynamically
* allocated .
* The format is " {addr|'*'}:port[-end][,{addr|'*'}:port[-end]]* " , where :
* - < addr > can be empty or " * " to indicate INADDR_ANY ;
* - < port > is a numerical port from 1 to 65535 ;
* - < end > indicates to use the range from < port > to < end > instead ( inclusive ) .
* This can be repeated as many times as necessary , separated by a coma .
* Function returns 1 for success or 0 if error . In case of errors , if < err > is
* not NULL , it must be a valid pointer to either NULL or a freeable area that
* will be replaced with an error message .
*/
int str2receiver ( char * str , struct proxy * curproxy , struct bind_conf * bind_conf , const char * file , int line , char * * err )
{
2020-09-16 11:58:55 -04:00
struct protocol * proto ;
2020-09-16 09:13:04 -04:00
char * next , * dupstr ;
int port , end ;
next = dupstr = strdup ( str ) ;
while ( next & & * next ) {
struct sockaddr_storage * ss2 ;
int fd = - 1 ;
str = next ;
/* 1) look for the end of the first address */
if ( ( next = strchr ( str , ' , ' ) ) ! = NULL ) {
* next + + = 0 ;
}
2023-11-09 05:19:24 -05:00
ss2 = str2sa_range ( str , NULL , & port , & end , & fd , & proto , NULL , err ,
2021-03-13 05:00:33 -05:00
curproxy = = global . cli_fe ? NULL : global . unix_bind . prefix ,
2024-08-26 05:50:24 -04:00
NULL , NULL , PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_PORT_RANGE |
2020-09-16 09:13:04 -04:00
PA_O_SOCKET_FD | PA_O_DGRAM | PA_O_XPRT ) ;
if ( ! ss2 )
goto fail ;
/* OK the address looks correct */
2020-09-16 11:58:55 -04:00
if ( ! create_listeners ( bind_conf , ss2 , port , end , fd , proto , err ) ) {
2020-09-16 09:13:04 -04:00
memprintf ( err , " %s for address '%s'. \n " , * err , str ) ;
goto fail ;
}
} /* end while(next) */
free ( dupstr ) ;
return 1 ;
fail :
free ( dupstr ) ;
return 0 ;
}
2021-05-08 13:58:37 -04:00
/*
* Sends a warning if proxy < proxy > does not have at least one of the
* capabilities in < cap > . An optional < hint > may be added at the end
* of the warning to help the user . Returns 1 if a warning was emitted
* or 0 if the condition is valid .
*/
int warnifnotcap ( struct proxy * proxy , int cap , const char * file , int line , const char * arg , const char * hint )
{
char * msg ;
switch ( cap ) {
case PR_CAP_BE : msg = " no backend " ; break ;
case PR_CAP_FE : msg = " no frontend " ; break ;
case PR_CAP_BE | PR_CAP_FE : msg = " neither frontend nor backend " ; break ;
default : msg = " not enough " ; break ;
}
if ( ! ( proxy - > cap & cap ) ) {
ha_warning ( " parsing [%s:%d] : '%s' ignored because %s '%s' has %s capability.%s \n " ,
file , line , arg , proxy_type_str ( proxy ) , proxy - > id , msg , hint ? hint : " " ) ;
return 1 ;
}
return 0 ;
}
/*
* Sends an alert if proxy < proxy > does not have at least one of the
* capabilities in < cap > . An optional < hint > may be added at the end
* of the alert to help the user . Returns 1 if an alert was emitted
* or 0 if the condition is valid .
*/
int failifnotcap ( struct proxy * proxy , int cap , const char * file , int line , const char * arg , const char * hint )
{
char * msg ;
switch ( cap ) {
case PR_CAP_BE : msg = " no backend " ; break ;
case PR_CAP_FE : msg = " no frontend " ; break ;
case PR_CAP_BE | PR_CAP_FE : msg = " neither frontend nor backend " ; break ;
default : msg = " not enough " ; break ;
}
if ( ! ( proxy - > cap & cap ) ) {
ha_alert ( " parsing [%s:%d] : '%s' not allowed because %s '%s' has %s capability.%s \n " ,
file , line , arg , proxy_type_str ( proxy ) , proxy - > id , msg , hint ? hint : " " ) ;
return 1 ;
}
return 0 ;
}
2016-12-21 16:41:44 -05:00
/*
* Report an error in < msg > when there are too many arguments . This version is
* intended to be used by keyword parsers so that the message will be included
* into the general error message . The index is the current keyword in args .
* Return 0 if the number of argument is correct , otherwise build a message and
* return 1. Fill err_code with an ERR_ALERT and an ERR_FATAL if not null . The
* message may also be null , it will simply not be produced ( useful to check only ) .
* < msg > and < err_code > are only affected on error .
*/
int too_many_args_idx ( int maxarg , int index , char * * args , char * * msg , int * err_code )
{
int i ;
if ( ! * args [ index + maxarg + 1 ] )
return 0 ;
if ( msg ) {
* msg = NULL ;
memprintf ( msg , " %s " , args [ 0 ] ) ;
for ( i = 1 ; i < = index ; i + + )
memprintf ( msg , " %s %s " , * msg , args [ i ] ) ;
memprintf ( msg , " '%s' cannot handle unexpected argument '%s'. " , * msg , args [ index + maxarg + 1 ] ) ;
}
if ( err_code )
* err_code | = ERR_ALERT | ERR_FATAL ;
return 1 ;
}
/*
* same as too_many_args_idx with a 0 index
*/
int too_many_args ( int maxarg , char * * args , char * * msg , int * err_code )
{
return too_many_args_idx ( maxarg , 0 , args , msg , err_code ) ;
}
2015-04-28 10:55:23 -04:00
/*
* Report a fatal Alert when there is too much arguments
* The index is the current keyword in args
* Return 0 if the number of argument is correct , otherwise emit an alert and return 1
* Fill err_code with an ERR_ALERT and an ERR_FATAL
*/
int alertif_too_many_args_idx ( int maxarg , int index , const char * file , int linenum , char * * args , int * err_code )
{
char * kw = NULL ;
int i ;
if ( ! * args [ index + maxarg + 1 ] )
return 0 ;
memprintf ( & kw , " %s " , args [ 0 ] ) ;
for ( i = 1 ; i < = index ; i + + ) {
memprintf ( & kw , " %s %s " , kw , args [ i ] ) ;
}
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : '%s' cannot handle unexpected argument '%s'. \n " , file , linenum , kw , args [ index + maxarg + 1 ] ) ;
2015-04-28 10:55:23 -04:00
free ( kw ) ;
* err_code | = ERR_ALERT | ERR_FATAL ;
return 1 ;
}
/*
* same as alertif_too_many_args_idx with a 0 index
*/
int alertif_too_many_args ( int maxarg , const char * file , int linenum , char * * args , int * err_code )
{
return alertif_too_many_args_idx ( maxarg , 0 , file , linenum , args , err_code ) ;
}
2009-03-31 04:49:21 -04:00
2026-01-08 10:55:38 -05:00
/* Report it if a request ACL condition uses some keywords that are
* incompatible with the place where the ACL is used . It returns either 0 or
* ERR_WARN so that its result can be or ' ed with err_code . Note that < cond > may
* be NULL and then will be ignored . In case of error , < err > is dynamically
* allocated to contains a description .
2010-01-28 11:59:39 -05:00
*/
2026-01-08 10:55:38 -05:00
int warnif_cond_conflicts ( const struct acl_cond * cond , unsigned int where ,
char * * err )
2010-01-28 11:59:39 -05:00
{
2013-03-25 03:12:18 -04:00
const struct acl * acl ;
2013-03-31 16:59:32 -04:00
const char * kw ;
2010-01-28 11:59:39 -05:00
2013-03-25 03:12:18 -04:00
if ( ! cond )
2010-01-28 11:59:39 -05:00
return 0 ;
2013-03-25 03:12:18 -04:00
acl = acl_cond_conflicts ( cond , where ) ;
if ( acl ) {
2026-01-08 10:55:38 -05:00
if ( acl - > name & & * acl - > name ) {
memprintf ( err , " acl '%s' will never match because it only involves keywords that are incompatible with '%s' " ,
acl - > name , sample_ckp_names ( where ) ) ;
}
else {
memprintf ( err , " anonymous acl will never match because it uses keyword '%s' which is incompatible with '%s' " ,
LIST_ELEM ( acl - > expr . n , struct acl_expr * , list ) - > kw , sample_ckp_names ( where ) ) ;
}
2013-03-25 03:12:18 -04:00
return ERR_WARN ;
}
if ( ! acl_cond_kw_conflicts ( cond , where , & acl , & kw ) )
2010-01-31 09:43:27 -05:00
return 0 ;
2026-01-08 10:55:38 -05:00
if ( acl - > name & & * acl - > name ) {
memprintf ( err , " acl '%s' involves keywords '%s' which is incompatible with '%s' " ,
acl - > name , kw , sample_ckp_names ( where ) ) ;
}
else {
memprintf ( err , " anonymous acl involves keyword '%s' which is incompatible with '%s' " ,
kw , sample_ckp_names ( where ) ) ;
}
2010-01-31 09:43:27 -05:00
return ERR_WARN ;
}
2021-03-26 05:02:46 -04:00
/* Report it if an ACL uses a L6 sample fetch from an HTTP proxy. It returns
* either 0 or ERR_WARN so that its result can be or ' ed with err_code . Note that
* < cond > may be NULL and then will be ignored .
*/
int warnif_tcp_http_cond ( const struct proxy * px , const struct acl_cond * cond )
{
if ( ! cond | | px - > mode ! = PR_MODE_HTTP )
return 0 ;
if ( cond - > use & ( SMP_USE_L6REQ | SMP_USE_L6RES ) ) {
ha_warning ( " Proxy '%s': L6 sample fetches ignored on HTTP proxies (declared at %s:%d). \n " ,
px - > id , cond - > file , cond - > line ) ;
return ERR_WARN ;
}
return 0 ;
}
2021-03-12 03:08:04 -05:00
/* try to find in <list> the word that looks closest to <word> by counting
* transitions between letters , digits and other characters . Will return the
* best matching word if found , otherwise NULL . An optional array of extra
* words to compare may be passed in < extra > , but it must then be terminated
* by a NULL entry . If unused it may be NULL .
*/
const char * cfg_find_best_match ( const char * word , const struct list * list , int section , const char * * extra )
{
uint8_t word_sig [ 1024 ] ; // 0..25=letter, 26=digit, 27=other, 28=begin, 29=end
uint8_t list_sig [ 1024 ] ;
const struct cfg_kw_list * kwl ;
int index ;
const char * best_ptr = NULL ;
int dist , best_dist = INT_MAX ;
make_word_fingerprint ( word_sig , word ) ;
list_for_each_entry ( kwl , list , list ) {
for ( index = 0 ; kwl - > kw [ index ] . kw ! = NULL ; index + + ) {
if ( kwl - > kw [ index ] . section ! = section )
continue ;
make_word_fingerprint ( list_sig , kwl - > kw [ index ] . kw ) ;
dist = word_fingerprint_distance ( word_sig , list_sig ) ;
if ( dist < best_dist ) {
best_dist = dist ;
best_ptr = kwl - > kw [ index ] . kw ;
}
}
}
while ( extra & & * extra ) {
make_word_fingerprint ( list_sig , * extra ) ;
dist = word_fingerprint_distance ( word_sig , list_sig ) ;
if ( dist < best_dist ) {
best_dist = dist ;
best_ptr = * extra ;
}
extra + + ;
}
if ( best_dist > 2 * strlen ( word ) | | ( best_ptr & & best_dist > 2 * strlen ( best_ptr ) ) )
best_ptr = NULL ;
return best_ptr ;
}
2017-10-16 09:49:32 -04:00
/* Parse a string representing a process number or a set of processes. It must
2019-01-26 07:25:14 -05:00
* be " all " , " odd " , " even " , a number between 1 and < max > or a range with
2017-11-22 05:21:58 -05:00
* two such numbers delimited by a dash ( ' - ' ) . On success , it returns
* 0. otherwise it returns 1 with an error message in < err > .
2017-10-16 09:49:32 -04:00
*
* Note : this function can also be used to parse a thread number or a set of
* threads .
*/
2019-01-26 07:25:14 -05:00
int parse_process_number ( const char * arg , unsigned long * proc , int max , int * autoinc , char * * err )
2017-10-16 09:49:32 -04:00
{
2017-11-22 09:01:51 -05:00
if ( autoinc ) {
* autoinc = 0 ;
if ( strncmp ( arg , " auto: " , 5 ) = = 0 ) {
arg + = 5 ;
* autoinc = 1 ;
}
}
2017-10-16 09:49:32 -04:00
if ( strcmp ( arg , " all " ) = = 0 )
2017-11-22 05:21:58 -05:00
* proc | = ~ 0UL ;
2017-10-16 09:49:32 -04:00
else if ( strcmp ( arg , " odd " ) = = 0 )
2017-11-22 05:21:58 -05:00
* proc | = ~ 0UL / 3UL ; /* 0x555....555 */
2017-10-16 09:49:32 -04:00
else if ( strcmp ( arg , " even " ) = = 0 )
2017-11-22 05:21:58 -05:00
* proc | = ( ~ 0UL / 3UL ) < < 1 ; /* 0xAAA...AAA */
2017-10-16 09:49:32 -04:00
else {
2019-02-07 10:29:41 -05:00
const char * p , * dash = NULL ;
2017-11-22 04:24:40 -05:00
unsigned int low , high ;
2019-02-07 10:29:41 -05:00
for ( p = arg ; * p ; p + + ) {
if ( * p = = ' - ' & & ! dash )
dash = p ;
2020-02-25 02:16:33 -05:00
else if ( ! isdigit ( ( unsigned char ) * p ) ) {
2019-02-07 10:29:41 -05:00
memprintf ( err , " '%s' is not a valid number/range. " , arg ) ;
return - 1 ;
}
2017-11-22 05:21:58 -05:00
}
2017-11-22 04:24:40 -05:00
low = high = str2uic ( arg ) ;
2019-02-07 10:29:41 -05:00
if ( dash )
2019-01-26 07:25:14 -05:00
high = ( ( ! * ( dash + 1 ) ) ? max : str2uic ( dash + 1 ) ) ;
2017-11-22 10:38:49 -05:00
2017-11-22 04:24:40 -05:00
if ( high < low ) {
unsigned int swap = low ;
low = high ;
high = swap ;
}
2019-01-26 07:25:14 -05:00
if ( low < 1 | | low > max | | high > max ) {
2017-11-22 10:50:41 -05:00
memprintf ( err , " '%s' is not a valid number/range. "
" It supports numbers from 1 to %d. \n " ,
2019-01-26 07:25:14 -05:00
arg , max ) ;
2017-11-22 05:21:58 -05:00
return 1 ;
}
2017-11-22 04:24:40 -05:00
for ( ; low < = high ; low + + )
2017-11-22 05:21:58 -05:00
* proc | = 1UL < < ( low - 1 ) ;
2017-10-16 09:49:32 -04:00
}
2019-01-26 07:25:14 -05:00
* proc & = ~ 0UL > > ( LONGBITS - max ) ;
2017-11-22 04:24:40 -05:00
2017-11-22 05:21:58 -05:00
return 0 ;
2017-10-16 09:49:32 -04:00
}
2015-01-29 21:22:58 -05:00
/*
2015-04-14 10:35:22 -04:00
* Parse a line in a < listen > , < frontend > or < backend > section .
2015-01-29 21:22:58 -05:00
* Returns the error code , 0 if OK , or any combination of :
* - ERR_ABORT : must abort ASAP
* - ERR_FATAL : we can continue parsing but not start the service
* - ERR_WARN : a warning has been emitted
* - ERR_ALERT : an alert has been emitted
* Only the two first ones can stop processing , the two others are just
* indicators .
*/
int cfg_parse_mailers ( const char * file , int linenum , char * * args , int kwm )
{
static struct mailers * curmailers = NULL ;
struct mailer * newmailer = NULL ;
const char * err ;
int err_code = 0 ;
char * errmsg = NULL ;
if ( strcmp ( args [ 0 ] , " mailers " ) = = 0 ) { /* new mailers section */
if ( ! * args [ 1 ] ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : missing name for mailers section. \n " , file , linenum ) ;
2015-01-29 21:22:58 -05:00
err_code | = ERR_ALERT | ERR_ABORT ;
goto out ;
}
err = invalid_char ( args [ 1 ] ) ;
if ( err ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'. \n " ,
file , linenum , * err , args [ 0 ] , args [ 1 ] ) ;
2015-01-29 21:22:58 -05:00
err_code | = ERR_ALERT | ERR_ABORT ;
goto out ;
}
for ( curmailers = mailers ; curmailers ! = NULL ; curmailers = curmailers - > next ) {
/*
* If there are two proxies with the same name only following
* combinations are allowed :
*/
if ( strcmp ( curmailers - > id , args [ 1 ] ) = = 0 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " Parsing [%s:%d]: mailers section '%s' has the same name as another mailers section declared at %s:%d. \n " ,
file , linenum , args [ 1 ] , curmailers - > conf . file , curmailers - > conf . line ) ;
2015-05-26 04:35:50 -04:00
err_code | = ERR_ALERT | ERR_FATAL ;
2015-01-29 21:22:58 -05:00
}
}
2016-04-03 07:48:43 -04:00
if ( ( curmailers = calloc ( 1 , sizeof ( * curmailers ) ) ) = = NULL ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : out of memory. \n " , file , linenum ) ;
2015-01-29 21:22:58 -05:00
err_code | = ERR_ALERT | ERR_ABORT ;
goto out ;
}
curmailers - > next = mailers ;
mailers = curmailers ;
curmailers - > conf . file = strdup ( file ) ;
curmailers - > conf . line = linenum ;
curmailers - > id = strdup ( args [ 1 ] ) ;
2016-02-13 09:33:40 -05:00
curmailers - > timeout . mail = DEF_MAILALERTTIME ; /* XXX: Would like to Skip to the next alert, if any, ASAP.
* But need enough time so that timeouts don ' t occur
* during tcp procssing . For now just us an arbitrary default . */
2015-01-29 21:22:58 -05:00
}
else if ( strcmp ( args [ 0 ] , " mailer " ) = = 0 ) { /* mailer definition */
struct sockaddr_storage * sk ;
int port1 , port2 ;
struct protocol * proto ;
if ( ! * args [ 2 ] ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments. \n " ,
file , linenum , args [ 0 ] ) ;
2015-01-29 21:22:58 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
err = invalid_char ( args [ 1 ] ) ;
if ( err ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : character '%c' is not permitted in server name '%s'. \n " ,
file , linenum , * err , args [ 1 ] ) ;
2015-01-29 21:22:58 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
2016-04-03 07:48:43 -04:00
if ( ( newmailer = calloc ( 1 , sizeof ( * newmailer ) ) ) = = NULL ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : out of memory. \n " , file , linenum ) ;
2015-01-29 21:22:58 -05:00
err_code | = ERR_ALERT | ERR_ABORT ;
goto out ;
}
/* the mailers are linked backwards first */
curmailers - > count + + ;
newmailer - > next = curmailers - > mailer_list ;
curmailers - > mailer_list = newmailer ;
newmailer - > mailers = curmailers ;
newmailer - > conf . file = strdup ( file ) ;
newmailer - > conf . line = linenum ;
newmailer - > id = strdup ( args [ 1 ] ) ;
2023-11-09 05:19:24 -05:00
sk = str2sa_range ( args [ 2 ] , NULL , & port1 , & port2 , NULL , & proto , NULL ,
2024-08-26 05:50:24 -04:00
& errmsg , NULL , NULL , NULL ,
2020-09-16 13:17:08 -04:00
PA_O_RESOLVE | PA_O_PORT_OK | PA_O_PORT_MAND | PA_O_STREAM | PA_O_XPRT | PA_O_CONNECT ) ;
2015-01-29 21:22:58 -05:00
if ( ! sk ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : '%s %s' : %s \n " , file , linenum , args [ 0 ] , args [ 1 ] , errmsg ) ;
2015-01-29 21:22:58 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
2020-09-16 13:17:08 -04:00
if ( proto - > sock_prot ! = IPPROTO_TCP ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : '%s %s' : TCP not supported for this address family. \n " ,
file , linenum , args [ 0 ] , args [ 1 ] ) ;
2015-01-29 21:22:58 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
newmailer - > addr = * sk ;
newmailer - > proto = proto ;
2016-12-22 14:44:00 -05:00
newmailer - > xprt = xprt_get ( XPRT_RAW ) ;
2015-01-29 21:22:58 -05:00
newmailer - > sock_init_arg = NULL ;
2016-02-13 09:33:40 -05:00
}
else if ( strcmp ( args [ 0 ] , " timeout " ) = = 0 ) {
if ( ! * args [ 1 ] ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : '%s' expects 'mail' and <time> as arguments. \n " ,
file , linenum , args [ 0 ] ) ;
2016-02-13 09:33:40 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
else if ( strcmp ( args [ 1 ] , " mail " ) = = 0 ) {
const char * res ;
unsigned int timeout_mail ;
if ( ! * args [ 2 ] ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : '%s %s' expects <time> as argument. \n " ,
file , linenum , args [ 0 ] , args [ 1 ] ) ;
2016-02-13 09:33:40 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
res = parse_time_err ( args [ 2 ] , & timeout_mail , TIME_UNIT_MS ) ;
2019-06-07 13:00:37 -04:00
if ( res = = PARSE_TIME_OVER ) {
ha_alert ( " parsing [%s:%d]: timer overflow in argument <%s> to <%s %s>, maximum value is 2147483647 ms (~24.8 days). \n " ,
file , linenum , args [ 2 ] , args [ 0 ] , args [ 1 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
else if ( res = = PARSE_TIME_UNDER ) {
ha_alert ( " parsing [%s:%d]: timer underflow in argument <%s> to <%s %s>, minimum non-null value is 1 ms. \n " ,
file , linenum , args [ 2 ] , args [ 0 ] , args [ 1 ] ) ;
2016-02-13 09:33:40 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
2019-06-07 13:00:37 -04:00
else if ( res ) {
ha_alert ( " parsing [%s:%d]: unexpected character '%c' in argument to <%s %s>. \n " ,
file , linenum , * res , args [ 0 ] , args [ 1 ] ) ;
2016-02-13 09:33:40 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
curmailers - > timeout . mail = timeout_mail ;
} else {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : '%s' expects 'mail' and <time> as arguments got '%s'. \n " ,
2016-02-13 09:33:40 -05:00
file , linenum , args [ 0 ] , args [ 1 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
}
2015-01-29 21:22:58 -05:00
else if ( * args [ 0 ] ! = 0 ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : unknown keyword '%s' in '%s' section \n " , file , linenum , args [ 0 ] , cursection ) ;
2015-01-29 21:22:58 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
out :
free ( errmsg ) ;
return err_code ;
}
2006-06-25 20:48:02 -04:00
2014-11-17 09:11:45 -05:00
int
cfg_parse_netns ( const char * file , int linenum , char * * args , int kwm )
{
2019-05-22 13:24:06 -04:00
# ifdef USE_NS
2014-11-17 09:11:45 -05:00
const char * err ;
const char * item = args [ 0 ] ;
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( item , " namespace_list " ) = = 0 ) {
2014-11-17 09:11:45 -05:00
return 0 ;
}
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
else if ( strcmp ( item , " namespace " ) = = 0 ) {
2014-11-17 09:11:45 -05:00
size_t idx = 1 ;
const char * current ;
while ( * ( current = args [ idx + + ] ) ) {
err = invalid_char ( current ) ;
if ( err ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: character '%c' is not permitted in '%s' name '%s'. \n " ,
file , linenum , * err , item , current ) ;
2014-11-17 09:11:45 -05:00
return ERR_ALERT | ERR_FATAL ;
}
if ( netns_store_lookup ( current , strlen ( current ) ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: Namespace '%s' is already added. \n " ,
file , linenum , current ) ;
2014-11-17 09:11:45 -05:00
return ERR_ALERT | ERR_FATAL ;
}
if ( ! netns_store_insert ( current ) ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: Cannot open namespace '%s'. \n " ,
file , linenum , current ) ;
2014-11-17 09:11:45 -05:00
return ERR_ALERT | ERR_FATAL ;
}
}
}
return 0 ;
# else
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: namespace support is not compiled in. " ,
file , linenum ) ;
2014-11-17 09:11:45 -05:00
return ERR_ALERT | ERR_FATAL ;
# endif
}
2010-01-29 11:50:44 -05:00
int
cfg_parse_users ( const char * file , int linenum , char * * args , int kwm )
{
int err_code = 0 ;
const char * err ;
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( args [ 0 ] , " userlist " ) = = 0 ) { /* new userlist */
2010-01-29 11:50:44 -05:00
struct userlist * newul ;
if ( ! * args [ 1 ] ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: '%s' expects <name> as arguments. \n " ,
file , linenum , args [ 0 ] ) ;
2010-01-29 11:50:44 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
2015-04-28 10:55:23 -04:00
if ( alertif_too_many_args ( 1 , file , linenum , args , & err_code ) )
goto out ;
2010-01-29 11:50:44 -05:00
err = invalid_char ( args [ 1 ] ) ;
if ( err ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: character '%c' is not permitted in '%s' name '%s'. \n " ,
file , linenum , * err , args [ 0 ] , args [ 1 ] ) ;
2010-01-29 11:50:44 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
for ( newul = userlist ; newul ; newul = newul - > next )
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( newul - > name , args [ 1 ] ) = = 0 ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " parsing [%s:%d]: ignoring duplicated userlist '%s'. \n " ,
file , linenum , args [ 1 ] ) ;
2010-01-29 11:50:44 -05:00
err_code | = ERR_WARN ;
goto out ;
}
2016-04-03 07:48:43 -04:00
newul = calloc ( 1 , sizeof ( * newul ) ) ;
2010-01-29 11:50:44 -05:00
if ( ! newul ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: out of memory. \n " , file , linenum ) ;
2010-01-29 11:50:44 -05:00
err_code | = ERR_ALERT | ERR_ABORT ;
goto out ;
}
newul - > name = strdup ( args [ 1 ] ) ;
2014-01-22 12:38:02 -05:00
if ( ! newul - > name ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: out of memory. \n " , file , linenum ) ;
2010-01-29 11:50:44 -05:00
err_code | = ERR_ALERT | ERR_ABORT ;
2016-04-08 05:35:26 -04:00
free ( newul ) ;
2010-01-29 11:50:44 -05:00
goto out ;
}
newul - > next = userlist ;
userlist = newul ;
2026-01-05 23:22:12 -05:00
} else {
const struct cfg_kw_list * kwl ;
char * errmsg = NULL ;
int index ;
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
list_for_each_entry ( kwl , & cfg_keywords . list , list ) {
for ( index = 0 ; kwl - > kw [ index ] . kw ; index + + ) {
if ( ( kwl - > kw [ index ] . section & CFG_USERLIST ) & &
( strcmp ( kwl - > kw [ index ] . kw , args [ 0 ] ) = = 0 ) ) {
err_code | = kwl - > kw [ index ] . parse ( args , CFG_USERLIST , NULL , NULL , file , linenum , & errmsg ) ;
if ( errmsg ) {
ha_alert ( " parsing [%s:%d] : %s \n " , file , linenum , errmsg ) ;
ha_free ( & errmsg ) ;
}
goto out ;
}
}
2010-01-29 11:50:44 -05:00
}
2026-01-05 23:22:12 -05:00
ha_alert ( " parsing [%s:%d]: unknown keyword '%s' in '%s' section \n " , file , linenum , args [ 0 ] , " userlist " ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
}
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
out :
return err_code ;
}
2015-05-28 12:03:51 -04:00
2026-01-05 23:22:12 -05:00
int cfg_parse_users_group ( char * * args , int section_type , struct proxy * curproxy , const struct proxy * defproxy , const char * file , int linenum , char * * err )
{
int cur_arg ;
const char * err_str ;
struct auth_groups * ag ;
int err_code = 0 ;
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
if ( ! * args [ 1 ] ) {
ha_alert ( " parsing [%s:%d]: '%s' expects <name> as arguments. \n " ,
file , linenum , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
2014-01-22 12:38:02 -05:00
2026-01-05 23:22:12 -05:00
err_str = invalid_char ( args [ 1 ] ) ;
if ( err_str ) {
ha_alert ( " parsing [%s:%d]: character '%c' is not permitted in '%s' name '%s'. \n " ,
file , linenum , * err_str , args [ 0 ] , args [ 1 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
if ( ! userlist )
goto out ;
for ( ag = userlist - > groups ; ag ; ag = ag - > next )
if ( strcmp ( ag - > name , args [ 1 ] ) = = 0 ) {
ha_warning ( " parsing [%s:%d]: ignoring duplicated group '%s' in userlist '%s'. \n " ,
file , linenum , args [ 1 ] , userlist - > name ) ;
err_code | = ERR_ALERT ;
2010-01-29 11:50:44 -05:00
goto out ;
}
2026-01-05 23:22:12 -05:00
ag = calloc ( 1 , sizeof ( * ag ) ) ;
if ( ! ag ) {
ha_alert ( " parsing [%s:%d]: out of memory. \n " , file , linenum ) ;
err_code | = ERR_ALERT | ERR_ABORT ;
goto out ;
}
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
ag - > name = strdup ( args [ 1 ] ) ;
if ( ! ag - > name ) {
ha_alert ( " parsing [%s:%d]: out of memory. \n " , file , linenum ) ;
err_code | = ERR_ALERT | ERR_ABORT ;
free ( ag ) ;
goto out ;
}
cur_arg = 2 ;
while ( * args [ cur_arg ] ) {
if ( strcmp ( args [ cur_arg ] , " users " ) = = 0 ) {
if ( ag - > groupusers ) {
ha_alert ( " parsing [%s:%d]: 'users' option already defined in '%s' name '%s'. \n " ,
file , linenum , args [ 0 ] , args [ 1 ] ) ;
2010-01-29 11:50:44 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
2016-08-22 18:27:42 -04:00
free ( ag - > groupusers ) ;
free ( ag - > name ) ;
free ( ag ) ;
2010-01-29 11:50:44 -05:00
goto out ;
}
2026-01-05 23:22:12 -05:00
ag - > groupusers = strdup ( args [ cur_arg + 1 ] ) ;
cur_arg + = 2 ;
continue ;
} else {
ha_alert ( " parsing [%s:%d]: '%s' only supports 'users' option. \n " ,
file , linenum , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
free ( ag - > groupusers ) ;
free ( ag - > name ) ;
free ( ag ) ;
goto out ;
2010-01-29 11:50:44 -05:00
}
2026-01-05 23:22:12 -05:00
}
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
ag - > next = userlist - > groups ;
userlist - > groups = ag ;
2014-01-22 12:38:02 -05:00
2026-01-05 23:22:12 -05:00
out :
return err_code ;
}
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
int cfg_parse_users_user ( char * * args , int section_type , struct proxy * curproxy , const struct proxy * defproxy , const char * file , int linenum , char * * err )
{
struct auth_users * newuser ;
int cur_arg ;
int err_code = 0 ;
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
if ( ! * args [ 1 ] ) {
ha_alert ( " parsing [%s:%d]: '%s' expects <name> as arguments. \n " ,
file , linenum , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
if ( ! userlist )
goto out ;
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
for ( newuser = userlist - > users ; newuser ; newuser = newuser - > next )
if ( strcmp ( newuser - > user , args [ 1 ] ) = = 0 ) {
ha_warning ( " parsing [%s:%d]: ignoring duplicated user '%s' in userlist '%s'. \n " ,
file , linenum , args [ 1 ] , userlist - > name ) ;
err_code | = ERR_ALERT ;
2010-01-29 11:50:44 -05:00
goto out ;
}
2026-01-05 23:22:12 -05:00
newuser = calloc ( 1 , sizeof ( * newuser ) ) ;
if ( ! newuser ) {
ha_alert ( " parsing [%s:%d]: out of memory. \n " , file , linenum ) ;
err_code | = ERR_ALERT | ERR_ABORT ;
goto out ;
}
newuser - > user = strdup ( args [ 1 ] ) ;
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
newuser - > next = userlist - > users ;
userlist - > users = newuser ;
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
cur_arg = 2 ;
2010-01-29 11:50:44 -05:00
2026-01-05 23:22:12 -05:00
while ( * args [ cur_arg ] ) {
if ( strcmp ( args [ cur_arg ] , " password " ) = = 0 ) {
2019-05-22 13:24:06 -04:00
# ifdef USE_LIBCRYPT
2026-01-09 08:49:33 -05:00
struct timeval tv_before , tv_after ;
ulong ms_elapsed ;
gettimeofday ( & tv_before , NULL ) ;
2026-01-05 23:22:12 -05:00
if ( ! crypt ( " " , args [ cur_arg + 1 ] ) ) {
ha_alert ( " parsing [%s:%d]: the encrypted password used for user '%s' is not supported by crypt(3). \n " ,
file , linenum , newuser - > user ) ;
2010-01-29 11:50:44 -05:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
2026-01-09 08:49:33 -05:00
gettimeofday ( & tv_after , NULL ) ;
ms_elapsed = tv_ms_elapsed ( & tv_before , & tv_after ) ;
if ( ms_elapsed > = 10 ) {
ha_warning ( " parsing [%s:%d]: the hash algorithm used for this password takes %lu milliseconds to verify, which can have devastating performance and stability impacts. Please hash this password using a lighter algorithm (one that is compatible with web usage). \n " , file , linenum , ms_elapsed ) ;
err_code | = ERR_WARN ;
}
2026-01-05 23:22:12 -05:00
# else
ha_warning ( " parsing [%s:%d]: no crypt(3) support compiled, encrypted passwords will not work. \n " ,
file , linenum ) ;
err_code | = ERR_ALERT ;
# endif
newuser - > pass = strdup ( args [ cur_arg + 1 ] ) ;
cur_arg + = 2 ;
continue ;
} else if ( strcmp ( args [ cur_arg ] , " insecure-password " ) = = 0 ) {
newuser - > pass = strdup ( args [ cur_arg + 1 ] ) ;
newuser - > flags | = AU_O_INSECURE ;
cur_arg + = 2 ;
continue ;
} else if ( strcmp ( args [ cur_arg ] , " groups " ) = = 0 ) {
newuser - > u . groups_names = strdup ( args [ cur_arg + 1 ] ) ;
cur_arg + = 2 ;
continue ;
} else {
ha_alert ( " parsing [%s:%d]: '%s' only supports 'password', 'insecure-password' and 'groups' options. \n " ,
file , linenum , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
2010-01-29 11:50:44 -05:00
}
}
out :
return err_code ;
}
2006-06-25 20:48:02 -04:00
2016-11-04 17:36:15 -04:00
int
cfg_parse_scope ( const char * file , int linenum , char * line )
{
char * beg , * end , * scope = NULL ;
int err_code = 0 ;
const char * err ;
beg = line + 1 ;
end = strchr ( beg , ' ] ' ) ;
/* Detect end of scope declaration */
if ( ! end | | end = = beg ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : empty scope name is forbidden. \n " ,
file , linenum ) ;
2016-11-04 17:36:15 -04:00
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
/* Get scope name and check its validity */
scope = my_strndup ( beg , end - beg ) ;
err = invalid_char ( scope ) ;
if ( err ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : character '%c' is not permitted in a scope name. \n " ,
file , linenum , * err ) ;
2016-11-04 17:36:15 -04:00
err_code | = ERR_ALERT | ERR_ABORT ;
goto out ;
}
/* Be sure to have a scope declaration alone on its line */
line = end + 1 ;
while ( isspace ( ( unsigned char ) * line ) )
line + + ;
if ( * line & & * line ! = ' # ' & & * line ! = ' \n ' & & * line ! = ' \r ' ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d] : character '%c' is not permitted after scope declaration. \n " ,
file , linenum , * line ) ;
2016-11-04 17:36:15 -04:00
err_code | = ERR_ALERT | ERR_ABORT ;
goto out ;
}
/* We have a valid scope declaration, save it */
free ( cfg_scope ) ;
cfg_scope = scope ;
scope = NULL ;
out :
free ( scope ) ;
return err_code ;
}
2018-01-29 06:05:07 -05:00
int
cfg_parse_track_sc_num ( unsigned int * track_sc_num ,
const char * arg , const char * end , char * * errmsg )
{
const char * p ;
unsigned int num ;
p = arg ;
num = read_uint64 ( & arg , end ) ;
if ( arg ! = end ) {
memprintf ( errmsg , " Wrong track-sc number '%s' " , p ) ;
return - 1 ;
}
2023-01-06 10:09:58 -05:00
if ( num > = global . tune . nb_stk_ctr ) {
if ( ! global . tune . nb_stk_ctr )
memprintf ( errmsg , " %u track-sc number not usable, stick-counters "
" are disabled by tune.stick-counters " , num ) ;
else
memprintf ( errmsg , " %u track-sc number exceeding "
" %d (tune.stick-counters-1) value " , num , global . tune . nb_stk_ctr - 1 ) ;
2018-01-29 06:05:07 -05:00
return - 1 ;
}
* track_sc_num = num ;
return 0 ;
}
2021-03-31 05:43:47 -04:00
/*
* Detect a global section after a non - global one and output a diagnostic
* warning .
*/
2022-11-18 09:46:06 -05:00
static void check_section_position ( char * section_name , const char * file , int linenum )
2021-03-31 05:43:47 -04:00
{
2021-10-16 11:48:15 -04:00
if ( strcmp ( section_name , " global " ) = = 0 ) {
2022-11-18 09:46:06 -05:00
if ( ( global . mode & MODE_DIAG ) & & non_global_section_parsed = = 1 )
2021-03-31 05:43:47 -04:00
_ha_diag_warning ( " parsing [%s:%d] : global section detected after a non-global one, the prevalence of their statements is unspecified \n " , file , linenum ) ;
}
2022-11-18 09:46:06 -05:00
else if ( non_global_section_parsed = = 0 ) {
non_global_section_parsed = 1 ;
2021-03-31 05:43:47 -04:00
}
}
2021-04-27 14:29:11 -04:00
/* apply the current default_path setting for config file <file>, and
* optionally replace the current path to < origin > if not NULL while the
* default - path mode is set to " origin " . Errors are returned into an
* allocated string passed to < err > if it ' s not NULL . Returns 0 on failure
* or non - zero on success .
*/
static int cfg_apply_default_path ( const char * file , const char * origin , char * * err )
{
const char * beg , * end ;
/* make path start at <beg> and end before <end>, and switch it to ""
* if no slash was passed .
*/
beg = file ;
end = strrchr ( beg , ' / ' ) ;
if ( ! end )
end = beg ;
if ( ! * initial_cwd ) {
if ( getcwd ( initial_cwd , sizeof ( initial_cwd ) ) = = NULL ) {
if ( err )
memprintf ( err , " Impossible to retrieve startup directory name: %s " , strerror ( errno ) ) ;
return 0 ;
}
}
else if ( chdir ( initial_cwd ) = = - 1 ) {
if ( err )
memprintf ( err , " Impossible to get back to initial directory '%s': %s " , initial_cwd , strerror ( errno ) ) ;
return 0 ;
}
/* OK now we're (back) to initial_cwd */
switch ( default_path_mode ) {
case DEFAULT_PATH_CURRENT :
/* current_cwd never set, nothing to do */
return 1 ;
case DEFAULT_PATH_ORIGIN :
/* current_cwd set in the config */
if ( origin & &
snprintf ( current_cwd , sizeof ( current_cwd ) , " %s " , origin ) > sizeof ( current_cwd ) ) {
if ( err )
memprintf ( err , " Absolute path too long: '%s' " , origin ) ;
return 0 ;
}
break ;
case DEFAULT_PATH_CONFIG :
if ( end - beg > = sizeof ( current_cwd ) ) {
if ( err )
memprintf ( err , " Config file path too long, cannot use for relative paths: '%s' " , file ) ;
return 0 ;
}
memcpy ( current_cwd , beg , end - beg ) ;
current_cwd [ end - beg ] = 0 ;
break ;
case DEFAULT_PATH_PARENT :
if ( end - beg + 3 > = sizeof ( current_cwd ) ) {
if ( err )
memprintf ( err , " Config file path too long, cannot use for relative paths: '%s' " , file ) ;
return 0 ;
}
memcpy ( current_cwd , beg , end - beg ) ;
if ( end > beg )
memcpy ( current_cwd + ( end - beg ) , " /.. \0 " , 4 ) ;
else
memcpy ( current_cwd + ( end - beg ) , " .. \0 " , 3 ) ;
break ;
}
if ( * current_cwd & & chdir ( current_cwd ) = = - 1 ) {
if ( err )
memprintf ( err , " Impossible to get back to directory '%s': %s " , initial_cwd , strerror ( errno ) ) ;
return 0 ;
}
return 1 ;
}
/* parses a global "default-path" directive. */
static int cfg_parse_global_def_path ( char * * args , int section_type , struct proxy * curpx ,
const struct proxy * defpx , const char * file , int line ,
char * * err )
{
int ret = - 1 ;
/* "current", "config", "parent", "origin <path>" */
if ( strcmp ( args [ 1 ] , " current " ) = = 0 )
default_path_mode = DEFAULT_PATH_CURRENT ;
else if ( strcmp ( args [ 1 ] , " config " ) = = 0 )
default_path_mode = DEFAULT_PATH_CONFIG ;
else if ( strcmp ( args [ 1 ] , " parent " ) = = 0 )
default_path_mode = DEFAULT_PATH_PARENT ;
else if ( strcmp ( args [ 1 ] , " origin " ) = = 0 )
default_path_mode = DEFAULT_PATH_ORIGIN ;
else {
memprintf ( err , " %s default-path mode '%s' for '%s', supported modes include 'current', 'config', 'parent', and 'origin'. " , * args [ 1 ] ? " unsupported " : " missing " , args [ 1 ] , args [ 0 ] ) ;
goto end ;
}
if ( default_path_mode = = DEFAULT_PATH_ORIGIN ) {
if ( ! * args [ 2 ] ) {
memprintf ( err , " '%s %s' expects a directory as an argument. " , args [ 0 ] , args [ 1 ] ) ;
goto end ;
}
if ( ! cfg_apply_default_path ( file , args [ 2 ] , err ) ) {
memprintf ( err , " couldn't set '%s' to origin '%s': %s. " , args [ 0 ] , args [ 2 ] , * err ) ;
goto end ;
}
}
else if ( ! cfg_apply_default_path ( file , NULL , err ) ) {
memprintf ( err , " couldn't set '%s' to '%s': %s. " , args [ 0 ] , args [ 1 ] , * err ) ;
goto end ;
}
/* note that once applied, the path is immediately updated */
ret = 0 ;
end :
return ret ;
}
2024-08-07 12:20:43 -04:00
/* append a copy of string <filename>, ptr to some allocated memory at the at
* the end of the list < li > .
2024-08-07 12:12:48 -04:00
* On failure : return 0 and < err > filled with an error message .
2024-08-07 12:20:43 -04:00
* The caller is responsible for freeing the < err > and < filename > copy
* memory area using free ( ) .
2024-08-07 12:12:48 -04:00
*/
2024-08-07 12:20:43 -04:00
int list_append_cfgfile ( struct list * li , const char * filename , char * * err )
2024-08-07 12:12:48 -04:00
{
2024-08-07 12:20:43 -04:00
struct cfgfile * entry = NULL ;
2024-08-07 12:12:48 -04:00
2024-08-07 12:20:43 -04:00
entry = calloc ( 1 , sizeof ( * entry ) ) ;
if ( ! entry ) {
2024-08-07 12:12:48 -04:00
memprintf ( err , " out of memory " ) ;
2024-08-07 12:20:43 -04:00
goto fail_entry ;
2024-08-07 12:12:48 -04:00
}
2024-08-07 12:20:43 -04:00
entry - > filename = strdup ( filename ) ;
if ( ! entry - > filename ) {
2024-08-07 12:12:48 -04:00
memprintf ( err , " out of memory " ) ;
2024-08-07 12:20:43 -04:00
goto fail_entry_name ;
2024-08-07 12:12:48 -04:00
}
2024-08-07 12:20:43 -04:00
LIST_APPEND ( li , & entry - > list ) ;
2024-08-07 12:12:48 -04:00
return 1 ;
2024-08-07 12:20:43 -04:00
fail_entry_name :
free ( entry - > filename ) ;
fail_entry :
free ( entry ) ;
2024-08-07 12:12:48 -04:00
return 0 ;
}
2024-08-05 04:03:39 -04:00
/* loads the content of the given file in memory. On success, returns the number
* of bytes successfully stored at * cfg_content until EOF . On error , emits
* alerts , performs needed clean - up routines and returns - 1.
*/
ssize_t load_cfg_in_mem ( char * filename , char * * cfg_content )
{
size_t bytes_to_read = LINESIZE ;
size_t chunk_size = 0 ;
size_t read_bytes = 0 ;
2024-08-07 10:31:25 -04:00
struct stat file_stat ;
2024-08-05 04:03:39 -04:00
char * new_area ;
size_t ret = 0 ;
FILE * f ;
2024-08-07 10:31:25 -04:00
/* let's try to obtain the size, if regular file */
if ( stat ( filename , & file_stat ) ! = 0 ) {
ha_alert ( " stat() failed for configuration file %s : %s \n " ,
filename , strerror ( errno ) ) ;
return - 1 ;
}
2024-08-08 10:34:54 -04:00
if ( file_stat . st_size > chunk_size )
bytes_to_read = file_stat . st_size ;
2024-08-07 10:31:25 -04:00
2024-08-05 04:03:39 -04:00
if ( ( f = fopen ( filename , " r " ) ) = = NULL ) {
ha_alert ( " Could not open configuration file %s : %s \n " ,
filename , strerror ( errno ) ) ;
return - 1 ;
}
* cfg_content = NULL ;
while ( 1 ) {
2024-08-20 04:04:03 -04:00
if ( ! file_stat . st_size & & ( ( read_bytes + bytes_to_read ) > MAX_CFG_SIZE ) ) {
ha_alert ( " Loading %s: input is too large %ldMB, limited to %dMB. Exiting. \n " ,
filename , ( long ) ( read_bytes + bytes_to_read ) / ( 1024 * 1024 ) ,
MAX_CFG_SIZE / ( 1024 * 1024 ) ) ;
goto free_mem ;
}
2024-08-05 04:03:39 -04:00
if ( read_bytes + bytes_to_read > chunk_size ) {
chunk_size = ( read_bytes + bytes_to_read ) * 2 ;
new_area = realloc ( * cfg_content , chunk_size ) ;
if ( new_area = = NULL ) {
ha_alert ( " Loading %s: file too long, cannot allocate memory. \n " ,
filename ) ;
goto free_mem ;
}
* cfg_content = new_area ;
}
bytes_to_read = chunk_size - read_bytes ;
ret = fread ( * cfg_content + read_bytes , sizeof ( char ) , bytes_to_read , f ) ;
read_bytes + = ret ;
if ( ! ret | | feof ( f ) | | ferror ( f ) )
break ;
}
fclose ( f ) ;
return read_bytes ;
free_mem :
ha_free ( cfg_content ) ;
fclose ( f ) ;
return - 1 ;
}
2006-06-25 20:48:02 -04:00
/*
2024-08-07 10:53:50 -04:00
* This function parses the configuration file given in the argument .
* Returns the error code , 0 if OK , - 1 if we are run out of memory ,
2021-04-27 12:30:28 -04:00
* or any combination of :
2009-07-20 03:30:05 -04:00
* - ERR_ABORT : must abort ASAP
* - ERR_FATAL : we can continue parsing but not start the service
* - ERR_WARN : a warning has been emitted
* - ERR_ALERT : an alert has been emitted
* Only the two first ones can stop processing , the two others are just
* indicators .
2006-06-25 20:48:02 -04:00
*/
2024-08-05 04:04:03 -04:00
int parse_cfg ( const struct cfgfile * cfg )
2006-06-25 20:48:02 -04:00
{
2021-04-27 12:30:28 -04:00
char * thisline = NULL ;
2015-05-12 08:25:37 -04:00
int linesize = LINESIZE ;
2006-06-25 20:48:02 -04:00
int linenum = 0 ;
2009-07-20 03:30:05 -04:00
int err_code = 0 ;
2017-10-16 05:06:50 -04:00
struct cfg_section * cs = NULL , * pcs = NULL ;
2014-03-18 08:54:18 -04:00
struct cfg_section * ics ;
2015-05-12 08:25:37 -04:00
int readbytes = 0 ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
char * outline = NULL ;
size_t outlen = 0 ;
size_t outlinesize = 0 ;
2020-06-16 11:14:33 -04:00
int fatal = 0 ;
2020-06-22 16:57:45 -04:00
int missing_lf = - 1 ;
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
int nested_cond_lvl = 0 ;
enum nested_cond_state nested_conds [ MAXNESTEDCONDS ] ;
2021-04-27 14:29:11 -04:00
char * errmsg = NULL ;
2024-08-07 10:53:50 -04:00
const char * cur_position = cfg - > content ;
char * file = cfg - > filename ;
2015-05-12 08:25:37 -04:00
2021-05-06 04:04:45 -04:00
global . cfg_curr_line = 0 ;
global . cfg_curr_file = file ;
2015-05-12 08:25:37 -04:00
if ( ( thisline = malloc ( sizeof ( * thisline ) * linesize ) ) = = NULL ) {
2021-04-27 12:30:28 -04:00
ha_alert ( " Out of memory trying to allocate a buffer for a configuration line. \n " ) ;
err_code = - 1 ;
goto err ;
2015-05-12 08:25:37 -04:00
}
2014-03-18 08:54:18 -04:00
2021-04-27 14:29:11 -04:00
/* change to the new dir if required */
if ( ! cfg_apply_default_path ( file , NULL , & errmsg ) ) {
ha_alert ( " parsing [%s:%d]: failed to apply default-path: %s. \n " , file , linenum , errmsg ) ;
free ( errmsg ) ;
err_code = - 1 ;
goto err ;
}
2015-05-12 08:27:13 -04:00
next_line :
2024-08-07 10:53:50 -04:00
while ( fgets_from_mem ( thisline + readbytes , linesize - readbytes ,
& cur_position , cfg - > content + cfg - > size ) ) {
2009-06-14 05:39:52 -04:00
int arg , kwm = KWM_STD ;
2007-10-31 19:33:12 -04:00
char * end ;
char * args [ MAX_LINE_ARGS + 1 ] ;
char * line = thisline ;
2025-12-04 09:21:21 -05:00
const char * errptr = NULL ; /* first error from parse_line() */
2007-10-31 19:33:12 -04:00
2020-06-22 16:57:45 -04:00
if ( missing_lf ! = - 1 ) {
2020-08-18 16:00:04 -04:00
ha_alert ( " parsing [%s:%d]: Stray NUL character at position %d. \n " ,
file , linenum , ( missing_lf + 1 ) ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
2020-06-22 16:57:45 -04:00
missing_lf = - 1 ;
2020-08-18 16:00:04 -04:00
break ;
2020-06-22 16:57:45 -04:00
}
2006-06-25 20:48:02 -04:00
linenum + + ;
2021-05-06 04:04:45 -04:00
global . cfg_curr_line = linenum ;
2006-06-25 20:48:02 -04:00
2020-06-16 11:14:33 -04:00
if ( fatal > = 50 ) {
ha_alert ( " parsing [%s:%d]: too many fatal errors (%d), stopping now. \n " , file , linenum , fatal ) ;
break ;
}
2006-06-25 20:48:02 -04:00
end = line + strlen ( line ) ;
2015-05-12 08:25:37 -04:00
if ( end - line = = linesize - 1 & & * ( end - 1 ) ! = ' \n ' ) {
2007-10-31 19:33:12 -04:00
/* Check if we reached the limit and the last char is not \n.
* Watch out for the last line without the terminating ' \n ' !
*/
2015-05-12 08:25:37 -04:00
char * newline ;
int newlinesize = linesize * 2 ;
newline = realloc ( thisline , sizeof ( * thisline ) * newlinesize ) ;
if ( newline = = NULL ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: line too long, cannot allocate memory. \n " ,
file , linenum ) ;
2015-05-12 08:25:37 -04:00
err_code | = ERR_ALERT | ERR_FATAL ;
2020-06-16 11:14:33 -04:00
fatal + + ;
2020-06-25 03:37:54 -04:00
linenum - - ;
2015-05-12 08:25:37 -04:00
continue ;
}
readbytes = linesize - 1 ;
linesize = newlinesize ;
thisline = newline ;
2020-06-25 03:37:54 -04:00
linenum - - ;
2015-05-12 08:25:37 -04:00
continue ;
2007-10-31 19:33:12 -04:00
}
2015-05-12 08:25:37 -04:00
readbytes = 0 ;
2020-06-26 11:24:54 -04:00
if ( end > line & & * ( end - 1 ) = = ' \n ' ) {
2020-06-22 16:57:44 -04:00
/* kill trailing LF */
* ( end - 1 ) = 0 ;
}
2020-06-22 16:57:45 -04:00
else {
/* mark this line as truncated */
missing_lf = end - line ;
}
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
2006-06-25 20:48:02 -04:00
/* skip leading spaces */
2007-06-17 15:51:38 -04:00
while ( isspace ( ( unsigned char ) * line ) )
2006-06-25 20:48:02 -04:00
line + + ;
2015-05-05 11:37:14 -04:00
2018-11-15 17:04:19 -05:00
if ( * line = = ' [ ' ) { /* This is the beginning if a scope */
2016-11-04 17:36:15 -04:00
err_code | = cfg_parse_scope ( file , linenum , line ) ;
goto next_line ;
}
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
while ( 1 ) {
uint32_t err ;
2006-06-25 20:48:02 -04:00
2021-06-05 18:50:20 -04:00
arg = sizeof ( args ) / sizeof ( * args ) ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
outlen = outlinesize ;
2025-12-04 09:21:21 -05:00
errptr = NULL ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
err = parse_line ( line , outline , & outlen , args , & arg ,
PARSE_OPT_ENV | PARSE_OPT_DQUOTE | PARSE_OPT_SQUOTE |
2020-10-01 08:32:35 -04:00
PARSE_OPT_BKSLASH | PARSE_OPT_SHARP | PARSE_OPT_WORD_EXPAND ,
& errptr ) ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
if ( err & PARSE_ERR_QUOTE ) {
2020-06-25 03:15:40 -04:00
size_t newpos = sanitize_for_printing ( line , errptr - line , 80 ) ;
ha_alert ( " parsing [%s:%d]: unmatched quote at position %d: \n "
" %s \n %*s \n " , file , linenum , ( int ) ( errptr - thisline + 1 ) , line , ( int ) ( newpos + 1 ) , " ^ " ) ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
err_code | = ERR_ALERT | ERR_FATAL ;
2020-06-16 11:14:33 -04:00
fatal + + ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
goto next_line ;
2015-05-05 11:37:14 -04:00
}
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
if ( err & PARSE_ERR_BRACE ) {
2020-06-25 03:15:40 -04:00
size_t newpos = sanitize_for_printing ( line , errptr - line , 80 ) ;
ha_alert ( " parsing [%s:%d]: unmatched brace in environment variable name at position %d: \n "
" %s \n %*s \n " , file , linenum , ( int ) ( errptr - thisline + 1 ) , line , ( int ) ( newpos + 1 ) , " ^ " ) ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
err_code | = ERR_ALERT | ERR_FATAL ;
2020-06-16 11:14:33 -04:00
fatal + + ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
goto next_line ;
2015-05-05 11:37:14 -04:00
}
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
if ( err & PARSE_ERR_VARNAME ) {
2020-06-25 03:15:40 -04:00
size_t newpos = sanitize_for_printing ( line , errptr - line , 80 ) ;
ha_alert ( " parsing [%s:%d]: forbidden first char in environment variable name at position %d: \n "
" %s \n %*s \n " , file , linenum , ( int ) ( errptr - thisline + 1 ) , line , ( int ) ( newpos + 1 ) , " ^ " ) ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
err_code | = ERR_ALERT | ERR_FATAL ;
2020-06-16 11:14:33 -04:00
fatal + + ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
goto next_line ;
2006-06-25 20:48:02 -04:00
}
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
if ( err & PARSE_ERR_HEX ) {
2020-06-25 03:15:40 -04:00
size_t newpos = sanitize_for_printing ( line , errptr - line , 80 ) ;
ha_alert ( " parsing [%s:%d]: truncated or invalid hexadecimal sequence at position %d: \n "
" %s \n %*s \n " , file , linenum , ( int ) ( errptr - thisline + 1 ) , line , ( int ) ( newpos + 1 ) , " ^ " ) ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
err_code | = ERR_ALERT | ERR_FATAL ;
2020-06-16 12:14:21 -04:00
fatal + + ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
goto next_line ;
2006-06-25 20:48:02 -04:00
}
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
2020-10-01 08:32:35 -04:00
if ( err & PARSE_ERR_WRONG_EXPAND ) {
size_t newpos = sanitize_for_printing ( line , errptr - line , 80 ) ;
ha_alert ( " parsing [%s:%d]: truncated or invalid word expansion sequence at position %d: \n "
" %s \n %*s \n " , file , linenum , ( int ) ( errptr - thisline + 1 ) , line , ( int ) ( newpos + 1 ) , " ^ " ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
fatal + + ;
goto next_line ;
}
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
if ( err & ( PARSE_ERR_TOOLARGE | PARSE_ERR_OVERLAP ) ) {
outlinesize = ( outlen + 1023 ) & - 1024 ;
2021-01-07 12:45:13 -05:00
outline = my_realloc2 ( outline , outlinesize ) ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
if ( outline = = NULL ) {
ha_alert ( " parsing [%s:%d]: line too long, cannot allocate memory. \n " ,
file , linenum ) ;
2022-05-20 03:13:38 -04:00
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
2020-06-16 11:14:33 -04:00
fatal + + ;
2022-05-18 10:22:43 -04:00
outlinesize = 0 ;
2022-05-20 03:13:38 -04:00
goto err ;
2015-05-12 08:27:13 -04:00
}
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
/* try again */
continue ;
2006-06-25 20:48:02 -04:00
}
2020-06-25 01:41:22 -04:00
if ( err & PARSE_ERR_TOOMANY ) {
/* only check this *after* being sure the output is allocated */
ha_alert ( " parsing [%s:%d]: too many words, truncating after word %d, position %ld: <%s>. \n " ,
file , linenum , MAX_LINE_ARGS , ( long ) ( args [ MAX_LINE_ARGS - 1 ] - outline + 1 ) , args [ MAX_LINE_ARGS - 1 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
fatal + + ;
goto next_line ;
}
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
/* everything's OK */
break ;
2015-05-05 11:37:14 -04:00
}
2006-06-25 20:48:02 -04:00
2022-09-14 11:51:55 -04:00
/* dump cfg */
if ( global . mode & MODE_DUMP_CFG ) {
if ( args [ 0 ] ! = NULL ) {
struct cfg_section * sect ;
int is_sect = 0 ;
int i = 0 ;
uint32_t g_key = HA_ATOMIC_LOAD ( & global . anon_key ) ;
2022-09-29 04:34:04 -04:00
if ( global . mode & MODE_DUMP_NB_L )
qfprintf ( stdout , " %d \t " , linenum ) ;
2022-09-14 11:51:55 -04:00
/* if a word is in sections list, is_sect = 1 */
list_for_each_entry ( sect , & sections , list ) {
2025-02-10 09:07:05 -05:00
/* look for a section_name, but also a section_parser, because there might be
* only a post_section_parser */
if ( strcmp ( args [ 0 ] , sect - > section_name ) = = 0 & &
sect - > section_parser ) {
2022-09-14 11:51:55 -04:00
is_sect = 1 ;
break ;
}
}
if ( g_key = = 0 ) {
/* no anonymizing needed, dump the config as-is (but without comments).
* Note : tabs were lost during tokenizing , so we reinsert for non - section
* keywords .
*/
if ( ! is_sect )
qfprintf ( stdout , " \t " ) ;
for ( i = 0 ; i < arg ; i + + ) {
qfprintf ( stdout , " %s " , args [ i ] ) ;
}
qfprintf ( stdout , " \n " ) ;
continue ;
}
/* We're anonymizing */
if ( is_sect ) {
/* new sections are optionally followed by an identifier */
if ( arg > = 2 ) {
qfprintf ( stdout , " %s %s \n " , args [ 0 ] , HA_ANON_ID ( g_key , args [ 1 ] ) ) ;
}
else {
qfprintf ( stdout , " %s \n " , args [ 0 ] ) ;
}
continue ;
}
/* non-section keywords start indented */
qfprintf ( stdout , " \t " ) ;
/* some keywords deserve special treatment */
if ( ! * args [ 0 ] ) {
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " anonkey " ) = = 0 ) {
qfprintf ( stdout , " %s [...] \n " , args [ 0 ] ) ;
}
else if ( strcmp ( args [ 0 ] , " maxconn " ) = = 0 ) {
qfprintf ( stdout , " %s %s \n " , args [ 0 ] , args [ 1 ] ) ;
}
else if ( strcmp ( args [ 0 ] , " stats " ) = = 0 & &
( strcmp ( args [ 1 ] , " timeout " ) = = 0 | | strcmp ( args [ 1 ] , " maxconn " ) = = 0 ) ) {
qfprintf ( stdout , " %s %s %s \n " , args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
}
else if ( strcmp ( args [ 0 ] , " stats " ) = = 0 & & strcmp ( args [ 1 ] , " socket " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , args [ 1 ] ) ;
2022-09-29 04:30:00 -04:00
if ( arg > 2 ) {
2022-09-29 04:25:31 -04:00
qfprintf ( stdout , " %s " , hash_ipanon ( g_key , args [ 2 ] , 1 ) ) ;
2022-09-14 11:51:55 -04:00
2022-09-29 04:30:00 -04:00
if ( arg > 3 ) {
2022-09-14 11:51:55 -04:00
qfprintf ( stdout , " [...] \n " ) ;
}
else {
qfprintf ( stdout , " \n " ) ;
}
}
else {
qfprintf ( stdout , " \n " ) ;
}
}
else if ( strcmp ( args [ 0 ] , " timeout " ) = = 0 ) {
qfprintf ( stdout , " %s %s %s \n " , args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
}
else if ( strcmp ( args [ 0 ] , " mode " ) = = 0 ) {
qfprintf ( stdout , " %s %s \n " , args [ 0 ] , args [ 1 ] ) ;
}
2022-10-29 00:34:32 -04:00
/* It concerns user in global section and in userlist */
2022-09-14 11:51:55 -04:00
else if ( strcmp ( args [ 0 ] , " user " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , HA_ANON_ID ( g_key , args [ 1 ] ) ) ;
if ( arg > 2 ) {
qfprintf ( stdout , " [...] \n " ) ;
}
else {
qfprintf ( stdout , " \n " ) ;
}
}
else if ( strcmp ( args [ 0 ] , " bind " ) = = 0 ) {
qfprintf ( stdout , " %s " , args [ 0 ] ) ;
2022-09-29 04:25:31 -04:00
qfprintf ( stdout , " %s " , hash_ipanon ( g_key , args [ 1 ] , 1 ) ) ;
2022-09-14 11:51:55 -04:00
if ( arg > 2 ) {
qfprintf ( stdout , " [...] \n " ) ;
}
else {
qfprintf ( stdout , " \n " ) ;
}
}
else if ( strcmp ( args [ 0 ] , " server " ) = = 0 ) {
2022-09-29 04:31:18 -04:00
qfprintf ( stdout , " %s %s " , args [ 0 ] , HA_ANON_ID ( g_key , args [ 1 ] ) ) ;
2022-09-14 11:51:55 -04:00
if ( arg > 2 ) {
2022-09-29 04:25:31 -04:00
qfprintf ( stdout , " %s " , hash_ipanon ( g_key , args [ 2 ] , 1 ) ) ;
2022-09-14 11:51:55 -04:00
}
if ( arg > 3 ) {
qfprintf ( stdout , " [...] \n " ) ;
}
else {
qfprintf ( stdout , " \n " ) ;
}
}
else if ( strcmp ( args [ 0 ] , " redirect " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , args [ 1 ] ) ;
if ( strcmp ( args [ 1 ] , " prefix " ) = = 0 | | strcmp ( args [ 1 ] , " location " ) = = 0 ) {
qfprintf ( stdout , " %s " , HA_ANON_PATH ( g_key , args [ 2 ] ) ) ;
}
else {
qfprintf ( stdout , " %s " , args [ 2 ] ) ;
}
if ( arg > 3 ) {
qfprintf ( stdout , " [...] " ) ;
}
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " acl " ) = = 0 ) {
qfprintf ( stdout , " %s %s %s " , args [ 0 ] , HA_ANON_ID ( g_key , args [ 1 ] ) , args [ 2 ] ) ;
if ( arg > 3 ) {
qfprintf ( stdout , " [...] " ) ;
}
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " log " ) = = 0 ) {
qfprintf ( stdout , " log " ) ;
if ( strcmp ( args [ 1 ] , " global " ) = = 0 ) {
qfprintf ( stdout , " %s " , args [ 1 ] ) ;
}
else {
2022-09-29 04:25:31 -04:00
qfprintf ( stdout , " %s " , hash_ipanon ( g_key , args [ 1 ] , 1 ) ) ;
2022-09-14 11:51:55 -04:00
}
if ( arg > 2 ) {
qfprintf ( stdout , " [...] " ) ;
}
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " peer " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , HA_ANON_ID ( g_key , args [ 1 ] ) ) ;
2022-09-29 04:25:31 -04:00
qfprintf ( stdout , " %s " , hash_ipanon ( g_key , args [ 2 ] , 1 ) ) ;
2022-09-14 11:51:55 -04:00
if ( arg > 3 ) {
qfprintf ( stdout , " [...] " ) ;
}
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " use_backend " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , HA_ANON_ID ( g_key , args [ 1 ] ) ) ;
if ( arg > 2 ) {
qfprintf ( stdout , " [...] " ) ;
}
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " default_backend " ) = = 0 ) {
qfprintf ( stdout , " %s %s \n " , args [ 0 ] , HA_ANON_ID ( g_key , args [ 1 ] ) ) ;
}
2022-09-29 04:31:18 -04:00
else if ( strcmp ( args [ 0 ] , " source " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , hash_ipanon ( g_key , args [ 1 ] , 1 ) ) ;
if ( arg > 2 ) {
qfprintf ( stdout , " [...] " ) ;
}
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " nameserver " ) = = 0 ) {
qfprintf ( stdout , " %s %s %s " , args [ 0 ] ,
HA_ANON_ID ( g_key , args [ 1 ] ) , hash_ipanon ( g_key , args [ 2 ] , 1 ) ) ;
if ( arg > 3 ) {
qfprintf ( stdout , " [...] " ) ;
}
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " http-request " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , args [ 1 ] ) ;
if ( arg > 2 )
qfprintf ( stdout , " [...] " ) ;
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " http-response " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , args [ 1 ] ) ;
if ( arg > 2 )
qfprintf ( stdout , " [...] " ) ;
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " http-after-response " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , args [ 1 ] ) ;
if ( arg > 2 )
qfprintf ( stdout , " [...] " ) ;
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " filter " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , args [ 1 ] ) ;
if ( arg > 2 )
qfprintf ( stdout , " [...] " ) ;
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " errorfile " ) = = 0 ) {
qfprintf ( stdout , " %s %s %s \n " , args [ 0 ] , args [ 1 ] , HA_ANON_PATH ( g_key , args [ 2 ] ) ) ;
}
else if ( strcmp ( args [ 0 ] , " cookie " ) = = 0 ) {
qfprintf ( stdout , " %s %s " , args [ 0 ] , HA_ANON_ID ( g_key , args [ 1 ] ) ) ;
if ( arg > 2 )
qfprintf ( stdout , " %s " , args [ 2 ] ) ;
if ( arg > 3 )
qfprintf ( stdout , " [...] " ) ;
qfprintf ( stdout , " \n " ) ;
}
else if ( strcmp ( args [ 0 ] , " stats " ) = = 0 & & strcmp ( args [ 1 ] , " auth " ) = = 0 ) {
qfprintf ( stdout , " %s %s %s \n " , args [ 0 ] , args [ 1 ] , HA_ANON_STR ( g_key , args [ 2 ] ) ) ;
}
2022-09-14 11:51:55 -04:00
else {
/* display up to 3 words and mask the rest which might be confidential */
for ( i = 0 ; i < MIN ( arg , 3 ) ; i + + ) {
qfprintf ( stdout , " %s " , args [ i ] ) ;
}
if ( arg > 3 ) {
qfprintf ( stdout , " [...] " ) ;
}
qfprintf ( stdout , " \n " ) ;
}
}
continue ;
}
/* end of config dump */
2006-06-25 20:48:02 -04:00
/* empty line */
2024-08-09 03:25:37 -04:00
if ( ! * args | | ! * * args )
2006-06-25 20:48:02 -04:00
continue ;
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
/* check for config macros */
if ( * args [ 0 ] = = ' . ' ) {
if ( strcmp ( args [ 0 ] , " .if " ) = = 0 ) {
2021-05-06 09:07:10 -04:00
const char * errptr = NULL ;
2021-05-06 02:19:48 -04:00
char * errmsg = NULL ;
int cond ;
2021-07-16 10:38:58 -04:00
char * w ;
2021-05-06 02:19:48 -04:00
2021-07-16 10:38:58 -04:00
/* remerge all words into a single expression */
for ( w = * args ; ( w + = strlen ( w ) ) < outline + outlen - 1 ; * w = ' ' )
;
2021-05-26 11:45:33 -04:00
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
nested_cond_lvl + + ;
if ( nested_cond_lvl > = MAXNESTEDCONDS ) {
ha_alert ( " parsing [%s:%d]: too many nested '.if', max is %d. \n " , file , linenum , MAXNESTEDCONDS ) ;
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
goto err ;
}
2021-05-06 02:46:11 -04:00
if ( nested_cond_lvl > 1 & &
( nested_conds [ nested_cond_lvl - 1 ] = = NESTED_COND_IF_DROP | |
nested_conds [ nested_cond_lvl - 1 ] = = NESTED_COND_IF_SKIP | |
nested_conds [ nested_cond_lvl - 1 ] = = NESTED_COND_ELIF_DROP | |
nested_conds [ nested_cond_lvl - 1 ] = = NESTED_COND_ELIF_SKIP | |
nested_conds [ nested_cond_lvl - 1 ] = = NESTED_COND_ELSE_DROP ) ) {
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
nested_conds [ nested_cond_lvl ] = NESTED_COND_IF_SKIP ;
2021-05-06 02:19:48 -04:00
goto next_line ;
}
2021-05-06 09:07:10 -04:00
cond = cfg_eval_condition ( args + 1 , & errmsg , & errptr ) ;
2021-05-06 02:19:48 -04:00
if ( cond < 0 ) {
2021-05-06 09:07:10 -04:00
size_t newpos = sanitize_for_printing ( args [ 1 ] , errptr - args [ 1 ] , 76 ) ;
ha_alert ( " parsing [%s:%d]: %s in '.if' at position %d: \n .if %s \n %*s \n " ,
file , linenum , errmsg ,
( int ) ( errptr - args [ 1 ] + 1 ) , args [ 1 ] , ( int ) ( newpos + 5 ) , " ^ " ) ;
2021-05-06 02:19:48 -04:00
free ( errmsg ) ;
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
goto err ;
}
2021-05-06 02:19:48 -04:00
if ( cond )
nested_conds [ nested_cond_lvl ] = NESTED_COND_IF_TAKE ;
else
nested_conds [ nested_cond_lvl ] = NESTED_COND_IF_DROP ;
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
goto next_line ;
}
else if ( strcmp ( args [ 0 ] , " .elif " ) = = 0 ) {
2021-05-06 09:07:10 -04:00
const char * errptr = NULL ;
2021-05-06 02:19:48 -04:00
char * errmsg = NULL ;
int cond ;
2021-07-16 10:38:58 -04:00
char * w ;
2021-05-06 02:19:48 -04:00
2021-07-16 10:38:58 -04:00
/* remerge all words into a single expression */
for ( w = * args ; ( w + = strlen ( w ) ) < outline + outlen - 1 ; * w = ' ' )
;
2021-05-26 11:45:33 -04:00
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
if ( ! nested_cond_lvl ) {
ha_alert ( " parsing [%s:%d]: lone '.elif' with no matching '.if'. \n " , file , linenum ) ;
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
goto err ;
}
if ( nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELSE_TAKE | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELSE_DROP ) {
ha_alert ( " parsing [%s:%d]: '.elif' after '.else' is not permitted. \n " , file , linenum ) ;
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
goto err ;
}
if ( nested_conds [ nested_cond_lvl ] = = NESTED_COND_IF_TAKE | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_IF_SKIP | |
2021-05-06 02:48:09 -04:00
nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELIF_TAKE | |
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELIF_SKIP ) {
nested_conds [ nested_cond_lvl ] = NESTED_COND_ELIF_SKIP ;
2021-05-06 02:19:48 -04:00
goto next_line ;
}
2021-05-06 09:07:10 -04:00
cond = cfg_eval_condition ( args + 1 , & errmsg , & errptr ) ;
2021-05-06 02:19:48 -04:00
if ( cond < 0 ) {
2021-05-06 09:07:10 -04:00
size_t newpos = sanitize_for_printing ( args [ 1 ] , errptr - args [ 1 ] , 74 ) ;
ha_alert ( " parsing [%s:%d]: %s in '.elif' at position %d: \n .elif %s \n %*s \n " ,
file , linenum , errmsg ,
( int ) ( errptr - args [ 1 ] + 1 ) , args [ 1 ] , ( int ) ( newpos + 7 ) , " ^ " ) ;
2021-05-06 02:19:48 -04:00
free ( errmsg ) ;
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
goto err ;
}
2021-05-06 02:19:48 -04:00
if ( cond )
nested_conds [ nested_cond_lvl ] = NESTED_COND_ELIF_TAKE ;
else
nested_conds [ nested_cond_lvl ] = NESTED_COND_ELIF_DROP ;
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
goto next_line ;
}
else if ( strcmp ( args [ 0 ] , " .else " ) = = 0 ) {
2021-05-26 11:45:33 -04:00
if ( * args [ 1 ] ) {
2021-06-12 06:55:27 -04:00
ha_alert ( " parsing [%s:%d]: Unexpected argument '%s' for '%s'. \n " ,
2021-05-26 11:45:33 -04:00
file , linenum , args [ 1 ] , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
break ;
}
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
if ( ! nested_cond_lvl ) {
ha_alert ( " parsing [%s:%d]: lone '.else' with no matching '.if'. \n " , file , linenum ) ;
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
goto err ;
}
if ( nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELSE_TAKE | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELSE_DROP ) {
ha_alert ( " parsing [%s:%d]: '.else' after '.else' is not permitted. \n " , file , linenum ) ;
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
goto err ;
}
if ( nested_conds [ nested_cond_lvl ] = = NESTED_COND_IF_TAKE | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_IF_SKIP | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELIF_TAKE | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELIF_SKIP ) {
nested_conds [ nested_cond_lvl ] = NESTED_COND_ELSE_DROP ;
} else {
/* otherwise we take the "else" */
nested_conds [ nested_cond_lvl ] = NESTED_COND_ELSE_TAKE ;
}
goto next_line ;
}
else if ( strcmp ( args [ 0 ] , " .endif " ) = = 0 ) {
2021-05-26 11:45:33 -04:00
if ( * args [ 1 ] ) {
2021-06-12 06:55:27 -04:00
ha_alert ( " parsing [%s:%d]: Unexpected argument '%s' for '%s'. \n " ,
2021-05-26 11:45:33 -04:00
file , linenum , args [ 1 ] , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
break ;
}
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
if ( ! nested_cond_lvl ) {
ha_alert ( " parsing [%s:%d]: lone '.endif' with no matching '.if'. \n " , file , linenum ) ;
2021-05-26 11:45:33 -04:00
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
break ;
}
nested_cond_lvl - - ;
goto next_line ;
}
}
if ( nested_cond_lvl & &
( nested_conds [ nested_cond_lvl ] = = NESTED_COND_IF_DROP | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_IF_SKIP | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELIF_DROP | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELIF_SKIP | |
nested_conds [ nested_cond_lvl ] = = NESTED_COND_ELSE_DROP ) ) {
/* The current block is masked out by the conditions */
goto next_line ;
}
2021-05-07 02:59:50 -04:00
/* .warning/.error/.notice/.diag */
2025-04-01 03:06:25 -04:00
if ( * args [ 0 ] = = ' . ' & & ! ( global . mode & MODE_DISCOVERY ) ) {
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
if ( strcmp ( args [ 0 ] , " .alert " ) = = 0 ) {
2021-05-26 11:45:33 -04:00
if ( * args [ 2 ] ) {
ha_alert ( " parsing [%s:%d]: Unexpected argument '%s' for '%s'. Use quotes if the message should contain spaces. \n " ,
file , linenum , args [ 2 ] , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto next_line ;
}
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
ha_alert ( " parsing [%s:%d]: '%s'. \n " , file , linenum , args [ 1 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
goto err ;
}
else if ( strcmp ( args [ 0 ] , " .warning " ) = = 0 ) {
2021-05-26 11:45:33 -04:00
if ( * args [ 2 ] ) {
ha_alert ( " parsing [%s:%d]: Unexpected argument '%s' for '%s'. Use quotes if the message should contain spaces. \n " ,
file , linenum , args [ 2 ] , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto next_line ;
}
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
ha_warning ( " parsing [%s:%d]: '%s'. \n " , file , linenum , args [ 1 ] ) ;
err_code | = ERR_WARN ;
goto next_line ;
}
else if ( strcmp ( args [ 0 ] , " .notice " ) = = 0 ) {
2021-05-26 11:45:33 -04:00
if ( * args [ 2 ] ) {
ha_alert ( " parsing [%s:%d]: Unexpected argument '%s' for '%s'. Use quotes if the message should contain spaces. \n " ,
file , linenum , args [ 2 ] , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto next_line ;
}
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
ha_notice ( " parsing [%s:%d]: '%s'. \n " , file , linenum , args [ 1 ] ) ;
goto next_line ;
}
2021-05-07 02:59:50 -04:00
else if ( strcmp ( args [ 0 ] , " .diag " ) = = 0 ) {
2021-05-26 11:45:33 -04:00
if ( * args [ 2 ] ) {
ha_alert ( " parsing [%s:%d]: Unexpected argument '%s' for '%s'. Use quotes if the message should contain spaces. \n " ,
file , linenum , args [ 2 ] , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
goto next_line ;
}
2021-05-07 02:59:50 -04:00
ha_diag_warning ( " parsing [%s:%d]: '%s'. \n " , file , linenum , args [ 1 ] ) ;
goto next_line ;
}
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
else {
ha_alert ( " parsing [%s:%d]: unknown directive '%s'. \n " , file , linenum , args [ 0 ] ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
fatal + + ;
break ;
}
}
2025-12-04 09:21:21 -05:00
/* now check for empty args on the line. Only do that in normal
* mode to prevent double display during discovery pass . It relies
* on errptr as returned by parse_line ( ) above .
*/
if ( ! ( global . mode & MODE_DISCOVERY ) ) {
int check_arg ;
for ( check_arg = 0 ; check_arg < arg ; check_arg + + ) {
if ( ! * args [ check_arg ] ) {
static int warned_empty ;
size_t newpos ;
int suggest = 0 ;
/* if an empty arg was found, its pointer should be in <errptr>, except
* for rare cases such as ' \x00 ' etc . We need to check errptr in any case
* and if it ' s not set , we ' ll fall back to args ' s position in the output
* string instead ( less accurate but still useful ) .
*/
if ( ! errptr ) {
newpos = args [ check_arg ] - outline ;
if ( newpos > = strlen ( line ) )
newpos = 0 ; // impossible to report anything, start at the beginning.
errptr = line + newpos ;
} else if ( isalnum ( ( uchar ) * errptr ) | | * errptr = = ' _ ' ) {
/* looks like an environment variable */
suggest = 1 ;
}
/* sanitize input line in-place */
newpos = sanitize_for_printing ( line , errptr - line , 80 ) ;
ha_alert ( " parsing [%s:%d]: argument number %d at position %d is empty and marks the end of the "
" argument list: \n %s \n %*s \n %s " ,
file , linenum , check_arg , ( int ) ( errptr - thisline + 1 ) , line , ( int ) ( newpos + 1 ) ,
" ^ " , ( warned_empty + + ) ? " " :
( " Aborting to prevent all subsequent arguments from being silently ignored. "
" If this is caused by an environment variable expansion, please have a look at section "
" 2.3 of the configuration manual to find solutions to address this. \n " ) ) ;
if ( suggest ) {
const char * end = errptr ;
struct ist alt ;
while ( isalnum ( ( uchar ) * end ) | | * end = = ' _ ' )
end + + ;
if ( end > errptr ) {
alt = env_suggest ( ist2 ( errptr , end - errptr ) ) ;
if ( isttest ( alt ) )
ha_notice ( " Hint: maybe you meant %.*s instead ? \n " , ( int ) istlen ( alt ) , istptr ( alt ) ) ;
}
}
err_code | = ERR_ALERT | ERR_FATAL ;
fatal + + ;
goto next_line ;
}
}
}
2009-06-14 05:39:52 -04:00
/* check for keyword modifiers "no" and "default" */
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( args [ 0 ] , " no " ) = = 0 ) {
2009-06-14 05:39:52 -04:00
kwm = KWM_NO ;
2026-01-15 10:41:38 -05:00
lshift_args ( args ) ;
2009-06-14 05:39:52 -04:00
}
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
else if ( strcmp ( args [ 0 ] , " default " ) = = 0 ) {
2009-06-14 05:39:52 -04:00
kwm = KWM_DEF ;
2026-01-15 10:41:38 -05:00
lshift_args ( args ) ;
2007-12-24 20:40:22 -05:00
}
2019-10-27 15:08:10 -04:00
if ( kwm ! = KWM_STD & & strcmp ( args [ 0 ] , " option " ) ! = 0 & &
strcmp ( args [ 0 ] , " log " ) ! = 0 & & strcmp ( args [ 0 ] , " busy-polling " ) ! = 0 & &
MEDIUM: init: prevent process and thread creation at runtime
Some concerns are regularly raised about the risk to inherit some Lua
files which make use of a fork (e.g. via os.execute()) as well as
whether or not some of bugs we fix might or not be exploitable to run
some code. Given that haproxy is event-driven, any foreground activity
completely stops processing and is easy to detect, but background
activity is a different story. A Lua script could very well discretely
fork a sub-process connecting to a remote location and taking commands,
and some injected code could also try to hide its activity by creating
a process or a thread without blocking the rest of the processing. While
such activities should be extremely limited when run in an empty chroot
without any permission, it would be better to get a higher assurance
they cannot happen.
This patch introduces something very simple: it limits the number of
processes and threads to zero in the workers after the last thread was
created. By doing so, it effectively instructs the system to fail on
any fork() or clone() syscall. Thus any undesired activity has to happen
in the foreground and is way easier to detect.
This will obviously break external checks (whose concept is already
totally insecure), and for this reason a new option
"insecure-fork-wanted" was added to disable this protection, and it
is suggested in the fork() error report from the checks. It is
obviously recommended not to use it and to reconsider the reasons
leading to it being enabled in the first place.
If for any reason we fail to disable forks, we still start because it
could be imaginable that some operating systems refuse to set this
limit to zero, but in this case we emit a warning, that may or may not
be reported since we're after the fork point. Ideally over the long
term it should be conditionned by strict-limits and cause a hard fail.
2019-12-03 01:07:36 -05:00
strcmp ( args [ 0 ] , " set-dumpable " ) ! = 0 & & strcmp ( args [ 0 ] , " strict-limits " ) ! = 0 & &
2021-03-26 13:50:33 -04:00
strcmp ( args [ 0 ] , " insecure-fork-wanted " ) ! = 0 & &
strcmp ( args [ 0 ] , " numa-cpu-mapping " ) ! = 0 ) {
2019-10-27 15:08:10 -04:00
ha_alert ( " parsing [%s:%d]: negation/default currently "
2019-10-27 15:08:11 -04:00
" supported only for options, log, busy-polling, "
2021-03-26 13:50:33 -04:00
" set-dumpable, strict-limits, insecure-fork-wanted "
" and numa-cpu-mapping. \n " , file , linenum ) ;
2009-07-20 03:30:05 -04:00
err_code | = ERR_ALERT | ERR_FATAL ;
2020-06-16 11:14:33 -04:00
fatal + + ;
2007-12-24 20:40:22 -05:00
}
2014-03-18 08:54:18 -04:00
/* detect section start */
list_for_each_entry ( ics , & sections , list ) {
2025-02-10 09:07:05 -05:00
if ( strcmp ( args [ 0 ] , ics - > section_name ) = = 0 & & ics - > section_parser ) {
2014-03-18 08:54:18 -04:00
cursection = ics - > section_name ;
2018-11-30 07:50:47 -05:00
pcs = cs ;
2014-03-18 08:54:18 -04:00
cs = ics ;
2021-05-06 04:04:45 -04:00
free ( global . cfg_curr_section ) ;
global . cfg_curr_section = strdup ( * args [ 1 ] ? args [ 1 ] : args [ 0 ] ) ;
2022-11-18 09:46:06 -05:00
check_section_position ( args [ 0 ] , file , linenum ) ;
2014-03-18 08:54:18 -04:00
break ;
}
2010-09-23 12:39:19 -04:00
}
2025-02-10 09:07:05 -05:00
if ( pcs ) {
struct cfg_section * psect ;
2020-06-16 11:14:33 -04:00
int status ;
2024-10-01 10:11:01 -04:00
2025-02-10 09:07:05 -05:00
/* look for every post_section_parser for the previous section name */
list_for_each_entry ( psect , & sections , list ) {
if ( strcmp ( pcs - > section_name , psect - > section_name ) = = 0 & &
psect - > post_section_parser ) {
2020-06-16 11:14:33 -04:00
2025-02-10 09:07:05 -05:00
/* don't call post_section_parser in MODE_DISCOVERY */
if ( global . mode & MODE_DISCOVERY )
goto section_parser ;
status = psect - > post_section_parser ( ) ;
err_code | = status ;
if ( status & ERR_FATAL )
fatal + + ;
if ( err_code & ERR_ABORT )
goto err ;
}
}
2018-11-30 07:50:47 -05:00
}
2018-12-02 03:37:38 -05:00
pcs = NULL ;
2018-11-30 07:50:47 -05:00
2025-02-12 06:09:05 -05:00
section_parser :
2017-10-16 05:06:50 -04:00
if ( ! cs ) {
2024-10-01 10:11:01 -04:00
/* ignore unknown section names during the first read in MODE_DISCOVERY */
if ( global . mode & MODE_DISCOVERY )
continue ;
2017-11-24 10:50:31 -05:00
ha_alert ( " parsing [%s:%d]: unknown keyword '%s' out of section. \n " , file , linenum , args [ 0 ] ) ;
2009-07-20 03:30:05 -04:00
err_code | = ERR_ALERT | ERR_FATAL ;
2020-06-16 11:14:33 -04:00
fatal + + ;
2017-10-16 05:06:50 -04:00
} else {
2020-06-16 11:14:33 -04:00
int status ;
2024-10-09 17:11:07 -04:00
/* read only the "global" and "program" sections in MODE_DISCOVERY */
if ( ( ( global . mode & MODE_DISCOVERY ) & & ( strcmp ( cs - > section_name , " global " ) ! = 0 )
& & ( strcmp ( cs - > section_name , " program " ) ! = 0 ) ) )
2024-10-01 10:11:01 -04:00
continue ;
2020-06-16 11:14:33 -04:00
status = cs - > section_parser ( file , linenum , args , kwm ) ;
err_code | = status ;
if ( status & ERR_FATAL )
fatal + + ;
2017-10-16 05:06:50 -04:00
if ( err_code & ERR_ABORT )
goto err ;
}
2006-06-25 20:48:02 -04:00
}
2017-10-16 05:06:50 -04:00
2020-06-22 16:57:45 -04:00
if ( missing_lf ! = - 1 ) {
2020-08-18 16:00:04 -04:00
ha_alert ( " parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d. \n " ,
file , linenum , ( missing_lf + 1 ) ) ;
err_code | = ERR_ALERT | ERR_FATAL ;
2020-06-22 16:57:45 -04:00
}
2021-05-06 04:04:45 -04:00
ha_free ( & global . cfg_curr_section ) ;
2025-02-12 06:31:11 -05:00
/* call post_section_parser of the last section when there is no more lines */
2025-02-10 09:07:05 -05:00
if ( cs ) {
struct cfg_section * psect ;
2025-02-17 04:59:46 -05:00
int status ;
2025-02-10 09:07:05 -05:00
2025-02-12 06:31:11 -05:00
/* don't call post_section_parser in MODE_DISCOVERY */
2025-02-10 09:07:05 -05:00
if ( ! ( global . mode & MODE_DISCOVERY ) ) {
list_for_each_entry ( psect , & sections , list ) {
if ( strcmp ( cs - > section_name , psect - > section_name ) = = 0 & &
psect - > post_section_parser ) {
2025-02-17 04:59:46 -05:00
status = psect - > post_section_parser ( ) ;
if ( status & ERR_FATAL )
fatal + + ;
err_code | = status ;
if ( err_code & ERR_ABORT )
goto err ;
2025-02-10 09:07:05 -05:00
}
}
}
2025-02-12 06:31:11 -05:00
}
2017-10-16 05:06:50 -04:00
MINOR: cfgparse: implement a simple if/elif/else/endif macro block handler
Very often, especially since reg-tests, it would be desirable to be able
to conditionally comment out a config block, such as removing an SSL
binding when SSL is disabled, or enabling HTX only for certain versions,
etc.
This patch introduces a very simple nested block management which takes
".if", ".elif", ".else" and ".endif" directives to take or ignore a block.
For now the conditions are limited to empty string or "0" for false versus
a non-nul integer for true, which already suffices to test environment
variables. Still, it needs to be a bit more advanced with defines, versions
etc.
A set of ".notice", ".warning" and ".alert" statements are provided to
emit messages, often in order to provide advice about how to fix certain
conditions.
2021-02-12 11:59:10 -05:00
if ( nested_cond_lvl ) {
ha_alert ( " parsing [%s:%d]: non-terminated '.if' block. \n " , file , linenum ) ;
err_code | = ERR_ALERT | ERR_FATAL | ERR_ABORT ;
}
2021-04-27 14:29:11 -04:00
2017-10-16 05:06:50 -04:00
err :
2021-02-20 04:46:51 -05:00
ha_free ( & cfg_scope ) ;
2008-01-22 10:44:08 -05:00
cursection = NULL ;
2015-05-12 08:25:37 -04:00
free ( thisline ) ;
BUG/MEDIUM: cfgparse: use parse_line() to expand/unquote/unescape config lines
Issue 22689 in oss-fuzz shows that specially crafted config files can take
a long time to process. This happens when variable expansion, backslash
escaping or unquoting causes calls to memmove() and possibly to realloc()
resulting in O(N^2) complexity with N following the line size.
By using parse_line() we now have a safe parser that remains in O(N)
regardless of the type of operation. Error reporting changed a little bit
since the errors are not reported anymore from the deepest parsing level.
As such we now report the beginning of the error. One benefit is that for
many invalid character sequences, the original line is shown and the first
bad char or sequence is designated with a caret ('^'), which tends to be
visually easier to spot, for example:
[ALERT] 167/170507 (14633) : parsing [mini5.cfg:19]: unmatched brace in environment variable name below:
"${VAR"}
^
or:
[ALERT] 167/170645 (14640) : parsing [mini5.cfg:18]: unmatched quote below:
timeout client 10s'
^
In case the target buffer is too short for the new line, the output buffer
is grown in 1kB chunks and kept till the end, so that it should not happen
too often.
Before this patch a test like below involving a 4 MB long line would take
138s to process, 98% of which were spent in __memmove_avx_unaligned_erms(),
and now it takes only 65 milliseconds:
$ perl -e 'print "\"\$A\""x1000000,"\n"' | ./haproxy -c -f /dev/stdin 2>/dev/null
This may be backported to stable versions after a long period of
observation to be sure nothing broke. It relies on patch "MINOR: tools:
add a new configurable line parse, parse_line()".
2020-06-16 10:32:59 -04:00
free ( outline ) ;
2021-05-06 04:04:45 -04:00
global . cfg_curr_line = 0 ;
global . cfg_curr_file = NULL ;
2009-07-20 03:30:05 -04:00
return err_code ;
2009-06-22 09:48:36 -04:00
}
2009-07-23 07:36:36 -04:00
/*
* Returns the error code , 0 if OK , or any combination of :
* - ERR_ABORT : must abort ASAP
* - ERR_FATAL : we can continue parsing but not start the service
* - ERR_WARN : a warning has been emitted
* - ERR_ALERT : an alert has been emitted
* Only the two first ones can stop processing , the two others are just
* indicators .
*/
2009-06-22 09:48:36 -04:00
int check_config_validity ( )
{
2026-01-30 10:31:04 -05:00
int cfgerr = 0 , ret ;
2026-02-03 05:09:42 -05:00
struct proxy * init_proxies_list = NULL , * defpx ;
2019-03-14 02:07:41 -04:00
struct stktable * t ;
2009-06-22 09:48:36 -04:00
struct server * newsrv = NULL ;
2024-10-23 12:18:48 -04:00
struct mt_list back ;
2009-07-23 07:36:36 -04:00
int err_code = 0 ;
2025-12-23 09:40:42 -05:00
/* Value forced to skip '1' due to an historical bug, see below for more details. */
unsigned int next_pxid = 2 ;
2012-09-13 11:54:29 -04:00
struct bind_conf * bind_conf ;
2016-11-22 18:41:28 -05:00
char * err ;
2017-10-23 08:36:34 -04:00
struct cfg_postparser * postparser ;
2020-12-23 10:51:12 -05:00
struct resolvers * curr_resolvers = NULL ;
2021-03-08 11:31:39 -05:00
int i ;
2006-06-25 20:48:02 -04:00
2012-09-13 11:54:29 -04:00
bind_conf = NULL ;
2006-06-25 20:48:02 -04:00
/*
* Now , check for the integrity of all that we have collected .
*/
2012-11-21 18:17:38 -05:00
if ( ! global . tune . max_http_hdr )
global . tune . max_http_hdr = MAX_HTTP_HDR ;
if ( ! global . tune . cookie_len )
global . tune . cookie_len = CAPTURE_LEN ;
2017-05-18 02:58:41 -04:00
if ( ! global . tune . requri_len )
global . tune . requri_len = REQURI_LEN ;
2024-05-24 03:46:49 -04:00
if ( ! global . thread_limit )
global . thread_limit = MAX_THREADS ;
# if defined(USE_THREAD)
if ( thread_cpus_enabled_at_boot > global . thread_limit )
thread_cpus_enabled_at_boot = global . thread_limit ;
# endif
2023-07-20 11:22:35 -04:00
if ( global . nbthread > global . thread_limit ) {
2024-05-24 03:46:49 -04:00
ha_warning ( " nbthread forced to a higher value (%d) than the configured thread-hard-limit (%d), enforcing the limit. "
" Please fix either value to remove this warning. \n " ,
global . nbthread , global . thread_limit ) ;
global . nbthread = global . thread_limit ;
}
2019-01-26 08:27:06 -05:00
2026-01-30 05:23:34 -05:00
if ( global . tune . bufsize_large > 0 ) {
if ( global . tune . bufsize_large = = global . tune . bufsize )
global . tune . bufsize_large = 0 ;
else if ( global . tune . bufsize_large < global . tune . bufsize ) {
ha_warning ( " tune.bufsize.large (%u) is lower than tune.bufsize (%u). large buffers support is disabled. "
" Please fix either value to remove this warning. \n " ,
global . tune . bufsize_large , global . tune . bufsize ) ;
global . tune . bufsize_large = 0 ;
2026-03-10 13:50:36 -04:00
err_code | = ERR_WARN ;
2026-01-30 05:23:34 -05:00
}
}
2026-03-09 02:38:22 -04:00
if ( global . tune . bufsize_small > 0 ) {
if ( global . tune . bufsize_small = = global . tune . bufsize )
global . tune . bufsize_small = 0 ;
else if ( global . tune . bufsize_small > global . tune . bufsize ) {
ha_warning ( " invalid small buffer size %d bytes which is greater to default bufsize %d bytes. \n " ,
global . tune . bufsize_small , global . tune . bufsize ) ;
2026-03-10 13:48:05 -04:00
global . tune . bufsize_small = 0 ;
err_code | = ERR_WARN ;
2026-03-09 02:38:22 -04:00
}
}
2023-07-20 11:22:35 -04:00
/* in the worst case these were supposed to be set in thread_detect_count() */
BUG_ON ( ! global . nbthread ) ;
BUG_ON ( ! global . nbtgroups ) ;
2021-09-22 06:07:23 -04:00
2021-09-27 04:10:26 -04:00
if ( thread_map_to_groups ( ) < 0 ) {
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
2017-11-24 11:34:44 -05:00
pool_head_requri = create_pool ( " requri " , global . tune . requri_len , MEM_F_SHARED ) ;
2017-07-05 07:33:16 -04:00
2017-11-24 11:34:44 -05:00
pool_head_capture = create_pool ( " capture " , global . tune . cookie_len , MEM_F_SHARED ) ;
2012-11-21 18:17:38 -05:00
2026-01-26 05:13:29 -05:00
/* both will have already emitted an error message if needed */
if ( ! pool_head_requri | | ! pool_head_capture ) {
err_code | = ERR_ALERT | ERR_FATAL ;
goto out ;
}
2014-01-22 12:38:02 -05:00
/* Post initialisation of the users and groups lists. */
err_code = userlist_postinit ( ) ;
if ( err_code ! = ERR_NONE )
goto out ;
2009-03-15 09:51:53 -04:00
/* first, we will invert the proxy list order */
curproxy = NULL ;
2017-11-24 10:54:05 -05:00
while ( proxies_list ) {
2009-03-15 09:51:53 -04:00
struct proxy * next ;
2017-11-24 10:54:05 -05:00
next = proxies_list - > next ;
proxies_list - > next = curproxy ;
curproxy = proxies_list ;
2009-03-15 09:51:53 -04:00
if ( ! next )
break ;
2017-11-24 10:54:05 -05:00
proxies_list = next ;
2009-03-15 09:51:53 -04:00
}
2025-04-17 04:42:25 -04:00
/*
* we must finish to initialize certain things on the servers ,
2025-05-09 14:27:29 -04:00
* as some of the fields may be accessed soon
2025-04-17 04:42:25 -04:00
*/
MT_LIST_FOR_EACH_ENTRY_LOCKED ( newsrv , & servers_list , global_list , back ) {
2025-08-08 06:22:13 -04:00
err_code | = srv_preinit ( newsrv ) ;
2025-07-30 09:10:27 -04:00
if ( err_code & ERR_CODE )
goto out ;
2025-04-17 04:42:25 -04:00
}
2026-02-03 05:09:42 -05:00
list_for_each_entry ( defpx , & defaults_list , el ) {
/* check validity for 'tcp-request' layer 4/5/6/7 rules */
cfgerr + = check_action_rules ( & defpx - > tcp_req . l4_rules , defpx , & err_code ) ;
cfgerr + = check_action_rules ( & defpx - > tcp_req . l5_rules , defpx , & err_code ) ;
cfgerr + = check_action_rules ( & defpx - > tcp_req . inspect_rules , defpx , & err_code ) ;
cfgerr + = check_action_rules ( & defpx - > tcp_rep . inspect_rules , defpx , & err_code ) ;
cfgerr + = check_action_rules ( & defpx - > http_req_rules , defpx , & err_code ) ;
cfgerr + = check_action_rules ( & defpx - > http_res_rules , defpx , & err_code ) ;
cfgerr + = check_action_rules ( & defpx - > http_after_res_rules , defpx , & err_code ) ;
2026-03-12 11:35:29 -04:00
# ifdef USE_QUIC
cfgerr + = check_action_rules ( & defpx - > quic_init_rules , defpx , & err_code ) ;
# endif
2026-02-03 05:09:42 -05:00
err = NULL ;
i = smp_resolve_args ( defpx , & 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 ( defpx ) ;
}
2026-03-18 11:22:11 -04:00
err_code | = proxy_check_http_errors ( defpx ) ;
2026-02-03 05:09:42 -05:00
}
2022-08-18 09:53:21 -04:00
/* starting to initialize the main proxies list */
init_proxies_list = proxies_list ;
init_proxies_list_stage1 :
for ( curproxy = init_proxies_list ; curproxy ; curproxy = curproxy - > next ) {
2025-04-17 11:05:07 -04:00
proxy_init_per_thr ( curproxy ) ;
2025-12-23 09:40:42 -05:00
/* Assign automatic UUID if unset except for internal proxies.
*
* WARNING proxy UUID initialization is buggy as value ' 1 ' is
2025-12-25 13:06:04 -05:00
* skipped if not explicitly used . This is an historical bug
2025-12-23 09:40:42 -05:00
* and should not be corrected to prevent breakage on future
* versions .
*/
2021-08-20 04:15:40 -04:00
if ( ! ( curproxy - > cap & PR_CAP_INT ) & & curproxy - > uuid < 0 ) {
2025-08-23 13:24:21 -04:00
next_pxid = proxy_get_next_id ( next_pxid ) ;
2025-08-23 13:57:29 -04:00
curproxy - > uuid = next_pxid ;
2025-08-23 13:45:03 -04:00
proxy_index_id ( curproxy ) ;
2025-12-23 09:40:42 -05:00
next_pxid + + ;
2009-10-04 17:04:08 -04:00
}
2010-02-05 14:58:27 -05:00
2021-08-26 09:59:44 -04:00
if ( curproxy - > mode = = PR_MODE_HTTP & & global . tune . bufsize > = ( 256 < < 20 ) & & ONLY_ONCE ( ) ) {
ha_alert ( " global.tune.bufsize must be below 256 MB when HTTP is in use (current value = %d). \n " ,
global . tune . bufsize ) ;
cfgerr + + ;
}
2026-02-13 09:38:08 -05:00
if ( curproxy - > mode = = PR_MODE_HTTP & & global . tune . bufsize_large > = ( 256 < < 20 ) & & ONLY_ONCE ( ) ) {
ha_alert ( " global.tune.bufsize_large must be below 256 MB when HTTP is in use (current value = %d). \n " ,
global . tune . bufsize_large ) ;
cfgerr + + ;
}
2021-08-26 09:59:44 -04:00
2021-10-06 08:24:19 -04:00
if ( curproxy - > flags & PR_FL_DISABLED ) {
2020-10-07 12:36:54 -04:00
/* ensure we don't keep listeners uselessly bound. We
* can ' t disable their listeners yet ( fdtab not
* allocated yet ) but let ' s skip them .
*/
2019-05-07 08:16:18 -04:00
if ( curproxy - > table ) {
2021-02-20 04:46:51 -05:00
ha_free ( & curproxy - > table - > peers . name ) ;
2019-05-07 08:16:18 -04:00
curproxy - > table - > peers . p = NULL ;
}
2006-06-25 20:48:02 -04:00
continue ;
}
2026-01-30 10:31:04 -05:00
ret = proxy_finalize ( curproxy , & err_code ) ;
if ( ret ) {
cfgerr + = ret ;
if ( err_code & ERR_FATAL )
2023-07-25 09:16:55 -04:00
goto out ;
2021-07-23 09:46:46 -04:00
}
2021-03-05 04:48:42 -05:00
}
2019-02-07 08:46:29 -05:00
2025-12-23 10:15:47 -05:00
/* Dynamic proxies IDs will never be lowered than this value. */
dynpx_next_id = next_pxid ;
2022-08-18 09:53:21 -04:00
/*
* We have just initialized the main proxies list
* we must also configure the log - forward proxies list
*/
if ( init_proxies_list = = proxies_list ) {
init_proxies_list = cfg_log_forward ;
2022-08-22 04:25:11 -04:00
/* check if list is not null to avoid infinite loop */
2022-09-13 10:16:30 -04:00
if ( init_proxies_list )
goto init_proxies_list_stage1 ;
}
if ( init_proxies_list = = cfg_log_forward ) {
init_proxies_list = sink_proxies_list ;
/* check if list is not null to avoid infinite loop */
2022-08-22 04:25:11 -04:00
if ( init_proxies_list )
goto init_proxies_list_stage1 ;
2022-08-18 09:53:21 -04:00
}
2021-03-05 04:48:42 -05:00
/***********************************************************/
/* At this point, target names have already been resolved. */
/***********************************************************/
2019-02-07 08:46:29 -05:00
2021-10-01 12:23:30 -04:00
idle_conn_task = task_new_anywhere ( ) ;
2021-03-08 11:31:39 -05:00
if ( ! idle_conn_task ) {
ha_alert ( " parsing : failed to allocate global idle connection task. \n " ) ;
cfgerr + + ;
}
else {
idle_conn_task - > process = srv_cleanup_idle_conns ;
idle_conn_task - > context = NULL ;
for ( i = 0 ; i < global . nbthread ; i + + ) {
2021-10-01 12:23:30 -04:00
idle_conns [ i ] . cleanup_task = task_new_on ( i ) ;
2021-03-08 11:31:39 -05:00
if ( ! idle_conns [ i ] . cleanup_task ) {
ha_alert ( " parsing : failed to allocate idle connection tasks for thread '%d'. \n " , i ) ;
cfgerr + + ;
break ;
2021-03-05 04:48:42 -05:00
}
2020-02-13 13:12:07 -05:00
2021-03-08 11:31:39 -05:00
idle_conns [ i ] . cleanup_task - > process = srv_cleanup_toremove_conns ;
idle_conns [ i ] . cleanup_task - > context = NULL ;
HA_SPIN_INIT ( & idle_conns [ i ] . idle_conns_lock ) ;
MT_LIST_INIT ( & idle_conns [ i ] . toremove_conns ) ;
2019-02-07 08:46:29 -05:00
}
2014-09-16 07:41:21 -04:00
}
/* perform the final checks before creating tasks */
2022-08-18 09:53:21 -04:00
/* starting to initialize the main proxies list */
init_proxies_list = proxies_list ;
init_proxies_list_stage2 :
for ( curproxy = init_proxies_list ; curproxy ; curproxy = curproxy - > next ) {
2014-09-16 07:41:21 -04:00
struct listener * listener ;
unsigned int next_id ;
2009-08-16 16:37:44 -04:00
2012-09-07 10:58:00 -04:00
/* Configure SSL for each bind line.
* Note : if configuration fails at some point , the - > ctx member
* remains NULL so that listeners can later detach .
*/
2012-09-13 11:54:29 -04:00
list_for_each_entry ( bind_conf , & curproxy - > conf . bind , by_fe ) {
2016-12-21 17:38:39 -05:00
if ( bind_conf - > xprt - > prepare_bind_conf & &
bind_conf - > xprt - > prepare_bind_conf ( bind_conf ) < 0 )
2012-09-07 10:58:00 -04:00
cfgerr + + ;
2023-01-12 12:39:42 -05:00
bind_conf - > analysers | = curproxy - > fe_req_ana ;
2023-01-12 12:52:23 -05:00
if ( ! bind_conf - > maxaccept )
bind_conf - > maxaccept = global . tune . maxaccept ? global . tune . maxaccept : MAX_ACCEPT ;
2023-01-12 13:10:17 -05:00
bind_conf - > accept = session_accept_fd ;
2023-01-12 13:37:07 -05:00
if ( curproxy - > options & PR_O_TCP_NOLING )
bind_conf - > options | = BC_O_NOLINGER ;
2023-01-12 13:40:42 -05:00
/* smart accept mode is automatic in HTTP mode */
if ( ( curproxy - > options2 & PR_O2_SMARTACC ) | |
( ( curproxy - > mode = = PR_MODE_HTTP | | ( bind_conf - > options & BC_O_USE_SSL ) ) & &
! ( curproxy - > no_options2 & PR_O2_SMARTACC ) ) )
bind_conf - > options | = BC_O_NOQUICKACK ;
2012-09-13 11:54:29 -04:00
}
2012-09-07 10:58:00 -04:00
2007-10-28 20:09:36 -04:00
/* adjust this proxy's listeners */
2023-08-17 04:15:09 -04:00
bind_conf = NULL ;
2009-10-04 17:04:08 -04:00
next_id = 1 ;
2012-09-20 10:48:07 -04:00
list_for_each_entry ( listener , & curproxy - > conf . listeners , by_fe ) {
2009-10-04 17:04:08 -04:00
if ( ! listener - > luid ) {
/* listener ID not set, use automatic numbering with first
* spare entry starting with next_luid .
*/
BUG/MINOR: listener: always assign distinct IDs to shards
When sharded listeners were introdcued in 2.5 with commit 6dfbef4145
("MEDIUM: listener: add the "shards" bind keyword"), a point was
overlooked regarding how IDs are assigned to listeners: they are just
duplicated! This means that if a "option socket-stats" is set and a
shard is configured, or multiple thread groups are enabled, then a stats
dump will produce several lines with exactly the same socket name and ID.
This patch tries to address this by trying to assign consecutive numbers
to these sockets. The usual algo is maintained, but with a preference for
the next number in a shard. This will help users reserve ranges for each
socket, for example by using multiples of 100 or 1000 on each bind line,
leaving enough room for all shards to be assigned.
The mechanism however is quite tricky, because the configured listener
currently ends up being the last one of the shard. This helps insert them
before the current position without having to revisit them. But here it
causes a difficulty which is that we'd like to restart from the current
ID and assign new ones on top of it. What is done is that the number is
passed between shards and the current one is cleared (and removed from
the tree) so that we instead insert the new one. It's tricky because of
the situation which depends whether it's the listener that was already
assigned on the bind line or not. But overall, always removing the entry,
always adding the new one when the ID is not zero, and passing them from
the reference to the next one does the trick.
This may be backported to all versions till 2.6.
2024-04-09 02:41:06 -04:00
if ( listener - > by_fe . p ! = & curproxy - > conf . listeners ) {
struct listener * prev_li = LIST_PREV ( & listener - > by_fe , typeof ( prev_li ) , by_fe ) ;
if ( prev_li - > luid )
next_id = prev_li - > luid + 1 ;
}
2025-08-23 13:25:03 -04:00
next_id = listener_get_next_id ( curproxy , next_id ) ;
2025-08-24 05:12:49 -04:00
listener - > luid = next_id ;
2025-08-23 13:37:26 -04:00
listener_index_id ( curproxy , listener ) ;
2009-10-04 17:04:08 -04:00
}
2010-02-05 14:58:27 -05:00
next_id + + ;
2009-10-04 17:04:08 -04:00
2009-10-04 09:43:17 -04:00
/* enable separate counters */
if ( curproxy - > options2 & PR_O2_SOCKSTAT ) {
CLEANUP: counters: move from 3 types to 2 types
We used to have 3 types of counters with a huge overlap :
- listener counters : stats collected for each bind line
- proxy counters : union of the frontend and backend counters
- server counters : stats collected per server
It happens that quite a good part was common between listeners and
proxies due to the frontend counters being updated at the two locations,
and that similarly the server and proxy counters were overlapping and
being updated together.
This patch cleans this up to propose only two types of counters :
- fe_counters: used by frontends and listeners, related to
incoming connections activity
- be_counters: used by backends and servers, related to outgoing
connections activity
This allowed to remove some non-sensical counters from both parts. For
frontends, the following entries were removed :
cum_lbconn, last_sess, nbpend_max, failed_conns, failed_resp,
retries, redispatches, q_time, c_time, d_time, t_time
For backends, this ones was removed : intercepted_req.
While doing this it was discovered that we used to incorrectly report
intercepted_req for backends in the HTML stats, which was always zero
since it's never updated.
Also it revealed a few inconsistencies (which were not fixed as they
are harmless). For example, backends count connections (cum_conn)
instead of sessions while servers count sessions and not connections.
Over the long term, some extra cleanups may be performed by having
some counters update functions touching both the server and backend
at the same time, as well as both the frontend and listener, to
ensure that all sides have all their stats properly filled. The stats
dump will also be able to factor the dump functions by counter types.
2016-11-25 08:44:52 -05:00
listener - > counters = calloc ( 1 , sizeof ( * listener - > counters ) ) ;
2012-10-29 11:51:55 -04:00
if ( ! listener - > name )
memprintf ( & listener - > name , " sock-%d " , listener - > luid ) ;
2009-10-04 09:43:17 -04:00
}
2012-09-22 13:11:47 -04:00
2022-01-25 11:48:47 -05:00
# ifdef USE_QUIC
2023-01-12 14:20:57 -05:00
if ( listener - > bind_conf - > xprt = = xprt_get ( XPRT_QUIC ) ) {
2023-10-25 04:52:23 -04:00
/* quic_conn are counted against maxconn. */
listener - > bind_conf - > options | = BC_O_XPRT_MAXCONN ;
2023-11-06 10:34:38 -05:00
listener - > rx . quic_curr_handshake = 0 ;
2023-11-08 08:29:31 -05:00
listener - > rx . quic_curr_accept = 0 ;
2023-10-25 04:52:23 -04:00
2023-08-17 04:15:09 -04:00
# ifdef USE_QUIC_OPENSSL_COMPAT
/* store the last checked bind_conf in bind_conf */
2025-10-24 07:49:23 -04:00
if ( ! ( quic_tune . fe . opts & QUIC_TUNE_FE_LISTEN_OFF ) & &
2023-08-17 04:15:09 -04:00
! ( global . tune . options & GTUNE_LIMITED_QUIC ) & &
listener - > bind_conf ! = bind_conf ) {
bind_conf = listener - > bind_conf ;
ha_alert ( " Binding [%s:%d] for %s %s: this SSL library does not support the "
" QUIC protocol. A limited compatibility layer may be enabled using "
" the \" limited-quic \" global option if desired. \n " ,
listener - > bind_conf - > file , listener - > bind_conf - > line ,
proxy_type_str ( curproxy ) , curproxy - > id ) ;
cfgerr + + ;
}
# endif
2022-09-29 12:31:24 -04:00
2022-01-25 10:21:47 -05:00
li_init_per_thr ( listener ) ;
}
2022-01-25 11:48:47 -05:00
# endif
2007-10-28 20:09:36 -04:00
}
2012-09-13 11:54:29 -04:00
/* Release unused SSL configs */
list_for_each_entry ( bind_conf , & curproxy - > conf . bind , by_fe ) {
2022-05-20 09:56:32 -04:00
if ( ! ( bind_conf - > options & BC_O_USE_SSL ) & & bind_conf - > xprt - > destroy_bind_conf )
2016-12-22 11:30:54 -05:00
bind_conf - > xprt - > destroy_bind_conf ( bind_conf ) ;
2012-09-13 11:54:29 -04:00
}
2012-09-07 10:58:00 -04:00
2025-12-02 11:29:19 -05:00
/* Create the task associated with the proxy. Only necessary
* for frontend or if a stick - table is defined .
*/
if ( ( curproxy - > cap & PR_CAP_FE ) | | ( curproxy - > table & & curproxy - > table - > current ) ) {
curproxy - > task = task_new_anywhere ( ) ;
if ( curproxy - > task ) {
curproxy - > task - > context = curproxy ;
curproxy - > task - > process = manage_proxy ;
}
else {
ha_alert ( " Proxy '%s': no more memory when trying to allocate the management task \n " ,
curproxy - > id ) ;
cfgerr + + ;
}
2011-07-25 10:33:49 -04:00
}
2014-09-16 07:21:03 -04:00
}
2022-08-18 09:53:21 -04:00
/*
* We have just initialized the main proxies list
* we must also configure the log - forward proxies list
*/
if ( init_proxies_list = = proxies_list ) {
init_proxies_list = cfg_log_forward ;
2022-08-22 04:25:11 -04:00
/* check if list is not null to avoid infinite loop */
if ( init_proxies_list )
goto init_proxies_list_stage2 ;
2022-08-18 09:53:21 -04:00
}
2025-04-17 11:16:44 -04:00
if ( init_proxies_list = = cfg_log_forward ) {
init_proxies_list = sink_proxies_list ;
/* check if list is not null to avoid infinite loop */
if ( init_proxies_list )
goto init_proxies_list_stage2 ;
}
2007-12-24 20:40:22 -05:00
/*
* Recount currently required checks .
*/
2017-11-24 10:54:05 -05:00
for ( curproxy = proxies_list ; curproxy ; curproxy = curproxy - > next ) {
2007-12-24 20:40:22 -05:00
int optnum ;
2009-01-18 15:44:07 -05:00
for ( optnum = 0 ; cfg_opts [ optnum ] . name ; optnum + + )
if ( curproxy - > options & cfg_opts [ optnum ] . val )
global . last_checks | = cfg_opts [ optnum ] . checks ;
2007-12-24 20:40:22 -05:00
2009-01-18 15:44:07 -05:00
for ( optnum = 0 ; cfg_opts2 [ optnum ] . name ; optnum + + )
if ( curproxy - > options2 & cfg_opts2 [ optnum ] . val )
global . last_checks | = cfg_opts2 [ optnum ] . checks ;
2007-12-24 20:40:22 -05:00
}
2017-07-13 03:07:09 -04:00
if ( cfg_peers ) {
struct peers * curpeers = cfg_peers , * * last ;
2011-09-07 15:24:49 -04:00
struct peer * p , * pb ;
2015-05-01 13:15:17 -04:00
/* Remove all peers sections which don't have a valid listener,
* which are not used by any table , or which are bound to more
* than one process .
2011-09-07 15:24:49 -04:00
*/
2017-07-13 03:07:09 -04:00
last = & cfg_peers ;
2011-09-07 15:24:49 -04:00
while ( * last ) {
2022-10-17 08:58:19 -04:00
struct peer * peer ;
2020-03-24 15:08:30 -04:00
struct stktable * t ;
2011-09-07 15:24:49 -04:00
curpeers = * last ;
2015-05-01 14:02:17 -04:00
2020-09-24 02:48:08 -04:00
if ( curpeers - > disabled ) {
2015-05-01 14:02:17 -04:00
/* the "disabled" keyword was present */
if ( curpeers - > peers_fe )
stop_proxy ( curpeers - > peers_fe ) ;
curpeers - > peers_fe = NULL ;
}
2019-10-04 02:30:04 -04:00
else if ( ! curpeers - > peers_fe | | ! curpeers - > peers_fe - > id ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Removing incomplete section 'peers %s' (no peer named '%s'). \n " ,
curpeers - > id , localpeer ) ;
2020-03-24 15:08:30 -04:00
if ( curpeers - > peers_fe )
stop_proxy ( curpeers - > peers_fe ) ;
curpeers - > peers_fe = NULL ;
2015-05-01 14:02:17 -04:00
}
else {
2019-02-12 13:12:32 -05:00
/* Initializes the transport layer of the server part of all the peers belonging to
* < curpeers > section if required .
* Note that - > srv is used by the local peer of a new process to connect to the local peer
* of an old process .
*/
2018-04-26 08:35:21 -04:00
p = curpeers - > remote ;
while ( p ) {
BUG/MINOR: peers: Improve detection of config errors in peers sections
There are several misuses in peers sections that are not detected during the
configuration parsing and that could lead to undefined behaviors or crashes.
First, only one listener is expected for a peers section. If several bind
lines or local peer definitions are used, an error is triggered. However, if
multiple addresses are set on the same bind line, there is no error while
only the last listener is properly configured. On the 2.8, there is no crash
but side effects are hardly predictable. On older version, HAProxy crashes
if an unconfigured listener is used.
Then, there is no check on remote peers name. It is unexpected to have same
name for several remote peers. There is now a test, performed during the
post-parsing, to verify all remote peer names are unique.
Finally, server parsing options for the peers sections are changed to be
sure a port is always defined, and not a port range or a port offset.
This patch fixes the issue #2066. It could be backported to all stable
versions.
2023-06-02 08:10:36 -04:00
struct peer * other_peer ;
for ( other_peer = curpeers - > remote ; other_peer & & other_peer ! = p ; other_peer = other_peer - > next ) {
if ( strcmp ( other_peer - > id , p - > id ) = = 0 ) {
ha_alert ( " Peer section '%s' [%s:%d]: another peer named '%s' was already defined at line %s:%d, please use distinct names. \n " ,
curpeers - > peers_fe - > id ,
p - > conf . file , p - > conf . line ,
other_peer - > id , other_peer - > conf . file , other_peer - > conf . line ) ;
cfgerr + + ;
break ;
}
}
2019-01-11 08:06:12 -05:00
if ( p - > srv ) {
2020-03-27 13:55:49 -04:00
if ( p - > srv - > use_ssl = = 1 & & xprt_get ( XPRT_SSL ) & & xprt_get ( XPRT_SSL ) - > prepare_srv )
2019-01-11 08:06:12 -05:00
cfgerr + = xprt_get ( XPRT_SSL ) - > prepare_srv ( p - > srv ) ;
}
2018-04-26 08:35:21 -04:00
p = p - > next ;
}
2019-02-12 13:12:32 -05:00
/* Configure the SSL bindings of the local peer if required. */
if ( ! LIST_ISEMPTY ( & curpeers - > peers_fe - > conf . bind ) ) {
struct list * l ;
struct bind_conf * bind_conf ;
l = & curpeers - > peers_fe - > conf . bind ;
bind_conf = LIST_ELEM ( l - > n , typeof ( bind_conf ) , by_fe ) ;
2022-07-05 10:00:56 -04:00
2022-07-26 13:03:51 -04:00
if ( curpeers - > local - > srv ) {
if ( curpeers - > local - > srv - > use_ssl = = 1 & & ! ( bind_conf - > options & BC_O_USE_SSL ) ) {
ha_warning ( " Peers section '%s': local peer have a non-SSL listener and a SSL server configured at line %s:%d. \n " ,
curpeers - > peers_fe - > id , curpeers - > local - > conf . file , curpeers - > local - > conf . line ) ;
}
else if ( curpeers - > local - > srv - > use_ssl ! = 1 & & ( bind_conf - > options & BC_O_USE_SSL ) ) {
ha_warning ( " Peers section '%s': local peer have a SSL listener and a non-SSL server configured at line %s:%d. \n " ,
curpeers - > peers_fe - > id , curpeers - > local - > conf . file , curpeers - > local - > conf . line ) ;
}
}
2023-04-22 17:52:17 -04:00
/* 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 ;
2022-07-05 10:00:56 -04:00
}
2019-02-12 13:12:32 -05:00
if ( bind_conf - > xprt - > prepare_bind_conf & &
bind_conf - > xprt - > prepare_bind_conf ( bind_conf ) < 0 )
cfgerr + + ;
}
2019-05-20 12:22:52 -04:00
if ( ! peers_init_sync ( curpeers ) | | ! peers_alloc_dcache ( curpeers ) ) {
2018-10-15 05:18:03 -04:00
ha_alert ( " Peers section '%s': out of memory, giving up on peers. \n " ,
curpeers - > id ) ;
cfgerr + + ;
break ;
}
2011-09-07 15:24:49 -04:00
last = & curpeers - > next ;
2022-10-17 08:58:19 -04:00
/* Ignore the peer shard greater than the number of peer shard for this section.
* Also ignore the peer shard of the local peer .
*/
for ( peer = curpeers - > remote ; peer ; peer = peer - > next ) {
if ( peer = = curpeers - > local ) {
if ( peer - > srv - > shard ) {
ha_warning ( " Peers section '%s': shard ignored for '%s' local peer \n " ,
curpeers - > id , peer - > id ) ;
peer - > srv - > shard = 0 ;
}
}
else if ( peer - > srv - > shard > curpeers - > nb_shards ) {
ha_warning ( " Peers section '%s': shard ignored for '%s' local peer because "
" %d shard value is greater than the section number of shards (%d) \n " ,
curpeers - > id , peer - > id , peer - > srv - > shard , curpeers - > nb_shards ) ;
peer - > srv - > shard = 0 ;
}
}
2011-09-07 15:24:49 -04:00
continue ;
}
2015-05-01 14:02:17 -04:00
/* clean what has been detected above */
2011-09-07 15:24:49 -04:00
p = curpeers - > remote ;
while ( p ) {
pb = p - > next ;
free ( p - > id ) ;
free ( p ) ;
p = pb ;
}
/* Destroy and unlink this curpeers section.
* Note : curpeers is backed up into * last .
*/
free ( curpeers - > id ) ;
curpeers = curpeers - > next ;
2020-03-24 15:08:30 -04:00
/* Reset any refereance to this peers section in the list of stick-tables */
for ( t = stktables_list ; t ; t = t - > next ) {
if ( t - > peers . p & & t - > peers . p = = * last )
t - > peers . p = NULL ;
}
2011-09-07 15:24:49 -04:00
free ( * last ) ;
* last = curpeers ;
}
}
2019-03-14 02:07:41 -04:00
for ( t = stktables_list ; t ; t = t - > next ) {
if ( t - > proxy )
continue ;
2023-11-02 13:34:51 -04:00
err = NULL ;
if ( ! stktable_init ( t , & err ) ) {
ha_alert ( " Parsing [%s:%d]: failed to initialize '%s' stick-table: %s. \n " , t - > conf . file , t - > conf . line , t - > id , err ) ;
ha_free ( & err ) ;
2019-03-14 02:07:41 -04:00
cfgerr + + ;
}
}
2015-05-01 13:09:08 -04:00
/* initialize stick-tables on backend capable proxies. This must not
* be done earlier because the data size may be discovered while parsing
* other proxies .
*/
2017-11-24 10:54:05 -05:00
for ( curproxy = proxies_list ; curproxy ; curproxy = curproxy - > next ) {
2021-10-06 08:24:19 -04:00
if ( ( curproxy - > flags & PR_FL_DISABLED ) | | ! curproxy - > table )
2015-05-01 13:09:08 -04:00
continue ;
2023-11-02 13:34:51 -04:00
err = NULL ;
if ( ! stktable_init ( curproxy - > table , & err ) ) {
ha_alert ( " Proxy '%s': failed to initialize stick-table: %s. \n " , curproxy - > id , err ) ;
ha_free ( & err ) ;
2015-05-01 13:09:08 -04:00
cfgerr + + ;
}
}
2015-01-29 21:22:58 -05:00
if ( mailers ) {
struct mailers * curmailers = mailers , * * last ;
struct mailer * m , * mb ;
/* Remove all mailers sections which don't have a valid listener.
* This can happen when a mailers section is never referenced .
*/
last = & mailers ;
while ( * last ) {
curmailers = * last ;
if ( curmailers - > users ) {
last = & curmailers - > next ;
continue ;
}
2017-11-24 10:50:31 -05:00
ha_warning ( " Removing incomplete section 'mailers %s'. \n " ,
curmailers - > id ) ;
2015-01-29 21:22:58 -05:00
m = curmailers - > mailer_list ;
while ( m ) {
mb = m - > next ;
free ( m - > id ) ;
free ( m ) ;
m = mb ;
}
/* Destroy and unlink this curmailers section.
* Note : curmailers is backed up into * last .
*/
free ( curmailers - > id ) ;
curmailers = curmailers - > next ;
free ( * last ) ;
* last = curmailers ;
}
}
2015-08-19 10:44:03 -04:00
/* Update server_state_file_name to backend name if backend is supposed to use
* a server - state file locally defined and none has been provided */
2017-11-24 10:54:05 -05:00
for ( curproxy = proxies_list ; curproxy ; curproxy = curproxy - > next ) {
2015-08-19 10:44:03 -04:00
if ( curproxy - > load_server_state_from_file = = PR_SRV_STATE_FILE_LOCAL & &
curproxy - > server_state_file_name = = NULL )
curproxy - > server_state_file_name = strdup ( curproxy - > id ) ;
}
2020-12-23 10:51:12 -05:00
list_for_each_entry ( curr_resolvers , & sec_resolvers , list ) {
2018-04-13 17:43:04 -04:00
if ( LIST_ISEMPTY ( & curr_resolvers - > nameservers ) ) {
2021-06-04 12:22:08 -04:00
ha_warning ( " resolvers '%s' [%s:%d] has no nameservers configured! \n " ,
2018-04-13 17:43:04 -04:00
curr_resolvers - > id , curr_resolvers - > conf . file ,
curr_resolvers - > conf . line ) ;
err_code | = ERR_WARN ;
}
}
2017-10-23 08:36:34 -04:00
list_for_each_entry ( postparser , & postparsers , list ) {
if ( postparser - > func )
cfgerr + = postparser - > func ( ) ;
}
2025-10-17 12:15:12 -04:00
if ( experimental_directives_allowed & &
! ( get_tainted ( ) & TAINTED_CONFIG_EXP_KW_DECLARED ) ) {
ha_warning ( " Option 'expose-experimental-directives' is set in the global section but is "
" no longer used. It is strongly recommended to remove it in order to avoid "
" using an experimental directive by accident in the future. \n " ) ;
err_code | = ERR_WARN ;
}
2009-07-23 07:36:36 -04:00
if ( cfgerr > 0 )
err_code | = ERR_ALERT | ERR_FATAL ;
out :
return err_code ;
2006-06-25 20:48:02 -04:00
}
[MEDIUM] add support for configuration keyword registration
Any module which needs configuration keywords may now dynamically
register a keyword in a given section, and associate it with a
configuration parsing function using cfg_register_keywords() from
a constructor function. This makes the configuration parser more
modular because it is not required anymore to touch cfg_parse.c.
Example :
static int parse_global_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in global section\n");
return 0;
}
static int parse_listen_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in listen section\n");
if (*args[1]) {
snprintf(err, errlen, "missing arg for listen_blah!!!");
return -1;
}
return 0;
}
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "blah", parse_global_blah },
{ CFG_LISTEN, "blah", parse_listen_blah },
{ 0, NULL, NULL },
}};
__attribute__((constructor))
static void __module_init(void)
{
cfg_register_keywords(&cfg_kws);
}
2008-07-09 13:39:06 -04:00
/*
* Registers the CFG keyword list < kwl > as a list of valid keywords for next
* parsing sessions .
*/
void cfg_register_keywords ( struct cfg_kw_list * kwl )
{
2021-04-21 01:32:39 -04:00
LIST_APPEND ( & cfg_keywords . list , & kwl - > list ) ;
[MEDIUM] add support for configuration keyword registration
Any module which needs configuration keywords may now dynamically
register a keyword in a given section, and associate it with a
configuration parsing function using cfg_register_keywords() from
a constructor function. This makes the configuration parser more
modular because it is not required anymore to touch cfg_parse.c.
Example :
static int parse_global_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in global section\n");
return 0;
}
static int parse_listen_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in listen section\n");
if (*args[1]) {
snprintf(err, errlen, "missing arg for listen_blah!!!");
return -1;
}
return 0;
}
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "blah", parse_global_blah },
{ CFG_LISTEN, "blah", parse_listen_blah },
{ 0, NULL, NULL },
}};
__attribute__((constructor))
static void __module_init(void)
{
cfg_register_keywords(&cfg_kws);
}
2008-07-09 13:39:06 -04:00
}
2006-06-25 20:48:02 -04:00
[MEDIUM] add support for configuration keyword registration
Any module which needs configuration keywords may now dynamically
register a keyword in a given section, and associate it with a
configuration parsing function using cfg_register_keywords() from
a constructor function. This makes the configuration parser more
modular because it is not required anymore to touch cfg_parse.c.
Example :
static int parse_global_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in global section\n");
return 0;
}
static int parse_listen_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in listen section\n");
if (*args[1]) {
snprintf(err, errlen, "missing arg for listen_blah!!!");
return -1;
}
return 0;
}
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "blah", parse_global_blah },
{ CFG_LISTEN, "blah", parse_listen_blah },
{ 0, NULL, NULL },
}};
__attribute__((constructor))
static void __module_init(void)
{
cfg_register_keywords(&cfg_kws);
}
2008-07-09 13:39:06 -04:00
/*
* Unregisters the CFG keyword list < kwl > from the list of valid keywords .
*/
void cfg_unregister_keywords ( struct cfg_kw_list * kwl )
{
2021-04-21 01:32:39 -04:00
LIST_DELETE ( & kwl - > list ) ;
[MEDIUM] add support for configuration keyword registration
Any module which needs configuration keywords may now dynamically
register a keyword in a given section, and associate it with a
configuration parsing function using cfg_register_keywords() from
a constructor function. This makes the configuration parser more
modular because it is not required anymore to touch cfg_parse.c.
Example :
static int parse_global_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in global section\n");
return 0;
}
static int parse_listen_blah(char **args, int section_type, struct proxy *curpx,
struct proxy *defpx, char *err, int errlen)
{
printf("parsing blah in listen section\n");
if (*args[1]) {
snprintf(err, errlen, "missing arg for listen_blah!!!");
return -1;
}
return 0;
}
static struct cfg_kw_list cfg_kws = {{ },{
{ CFG_GLOBAL, "blah", parse_global_blah },
{ CFG_LISTEN, "blah", parse_listen_blah },
{ 0, NULL, NULL },
}};
__attribute__((constructor))
static void __module_init(void)
{
cfg_register_keywords(&cfg_kws);
}
2008-07-09 13:39:06 -04:00
LIST_INIT ( & kwl - > list ) ;
}
2006-06-25 20:48:02 -04:00
2014-03-18 08:54:18 -04:00
/* this function register new section in the haproxy configuration file.
* < section_name > is the name of this new section and < section_parser >
* is the called parser . If two section declaration have the same name ,
* only the first declared is used .
*/
int cfg_register_section ( char * section_name ,
2017-10-16 05:06:50 -04:00
int ( * section_parser ) ( const char * , int , char * * , int ) ,
int ( * post_section_parser ) ( ) )
2014-03-18 08:54:18 -04:00
{
struct cfg_section * cs ;
2025-02-10 09:07:05 -05:00
if ( section_parser ) {
/* only checks if we register a section parser, not a post section callback */
list_for_each_entry ( cs , & sections , list ) {
if ( strcmp ( cs - > section_name , section_name ) = = 0 & & cs - > section_parser ) {
ha_alert ( " register section '%s': already registered. \n " , section_name ) ;
return 0 ;
}
2016-05-17 10:16:09 -04:00
}
}
2014-03-18 08:54:18 -04:00
cs = calloc ( 1 , sizeof ( * cs ) ) ;
if ( ! cs ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " register section '%s': out of memory. \n " , section_name ) ;
2014-03-18 08:54:18 -04:00
return 0 ;
}
cs - > section_name = section_name ;
cs - > section_parser = section_parser ;
2017-10-16 05:06:50 -04:00
cs - > post_section_parser = post_section_parser ;
2014-03-18 08:54:18 -04:00
2021-04-21 01:32:39 -04:00
LIST_APPEND ( & sections , & cs - > list ) ;
2014-03-18 08:54:18 -04:00
return 1 ;
}
2017-10-23 08:36:34 -04:00
/* this function register a new function which will be called once the haproxy
* configuration file has been parsed . It ' s useful to check dependencies
* between sections or to resolve items once everything is parsed .
*/
int cfg_register_postparser ( char * name , int ( * func ) ( ) )
{
struct cfg_postparser * cp ;
cp = calloc ( 1 , sizeof ( * cp ) ) ;
if ( ! cp ) {
2017-11-24 10:50:31 -05:00
ha_alert ( " register postparser '%s': out of memory. \n " , name ) ;
2017-10-23 08:36:34 -04:00
return 0 ;
}
cp - > name = name ;
cp - > func = func ;
2021-04-21 01:32:39 -04:00
LIST_APPEND ( & postparsers , & cp - > list ) ;
2017-10-23 08:36:34 -04:00
return 1 ;
}
2015-09-25 06:49:18 -04:00
/*
* free all config section entries
*/
void cfg_unregister_sections ( void )
{
struct cfg_section * cs , * ics ;
list_for_each_entry_safe ( cs , ics , & sections , list ) {
2021-04-21 01:32:39 -04:00
LIST_DELETE ( & cs - > list ) ;
2015-09-25 06:49:18 -04:00
free ( cs ) ;
}
}
2016-10-26 05:09:44 -04:00
void cfg_backup_sections ( struct list * backup_sections )
{
struct cfg_section * cs , * ics ;
list_for_each_entry_safe ( cs , ics , & sections , list ) {
2021-04-21 01:32:39 -04:00
LIST_DELETE ( & cs - > list ) ;
LIST_APPEND ( backup_sections , & cs - > list ) ;
2016-10-26 05:09:44 -04:00
}
}
void cfg_restore_sections ( struct list * backup_sections )
{
struct cfg_section * cs , * ics ;
list_for_each_entry_safe ( cs , ics , backup_sections , list ) {
2021-04-21 01:32:39 -04:00
LIST_DELETE ( & cs - > list ) ;
LIST_APPEND ( & sections , & cs - > list ) ;
2016-10-26 05:09:44 -04:00
}
}
2022-03-29 09:02:44 -04:00
/* dumps all registered keywords by section on stdout */
void cfg_dump_registered_keywords ( )
{
2025-05-23 04:49:33 -04:00
/* CFG_GLOBAL, CFG_LISTEN, CFG_USERLIST, CFG_PEERS, CFG_CRTLIST, CFG_CRTSTORE, CFG_TRACES, CFG_ACME */
const char * sect_names [ ] = { " " , " global " , " listen " , " userlist " , " peers " , " crt-list " , " crt-store " , " traces " , " acme " , 0 } ;
2022-03-29 09:02:44 -04:00
int section ;
int index ;
for ( section = 1 ; sect_names [ section ] ; section + + ) {
struct cfg_kw_list * kwl ;
2022-03-30 05:21:32 -04:00
const struct cfg_keyword * kwp , * kwn ;
2022-03-29 09:02:44 -04:00
printf ( " %s \n " , sect_names [ section ] ) ;
2022-03-30 05:21:32 -04:00
for ( kwn = kwp = NULL ; ; kwp = kwn ) {
list_for_each_entry ( kwl , & cfg_keywords . list , list ) {
for ( index = 0 ; kwl - > kw [ index ] . kw ! = NULL ; index + + )
if ( kwl - > kw [ index ] . section = = section & &
strordered ( kwp ? kwp - > kw : NULL , kwl - > kw [ index ] . kw , kwn ! = kwp ? kwn - > kw : NULL ) )
kwn = & kwl - > kw [ index ] ;
}
if ( kwn = = kwp )
break ;
printf ( " \t %s \n " , kwn - > kw ) ;
2022-03-29 09:02:44 -04:00
}
if ( section = = CFG_LISTEN ) {
/* there are plenty of other keywords there */
extern struct list tcp_req_conn_keywords , tcp_req_sess_keywords ,
tcp_req_cont_keywords , tcp_res_cont_keywords ;
extern struct bind_kw_list bind_keywords ;
extern struct srv_kw_list srv_keywords ;
struct bind_kw_list * bkwl ;
struct srv_kw_list * skwl ;
2022-03-30 05:21:32 -04:00
const struct bind_kw * bkwp , * bkwn ;
const struct srv_kw * skwp , * skwn ;
const struct cfg_opt * coptp , * coptn ;
2023-02-13 09:24:01 -05:00
/* display the non-ssl keywords */
2022-03-30 05:21:32 -04:00
for ( bkwn = bkwp = NULL ; ; bkwp = bkwn ) {
list_for_each_entry ( bkwl , & bind_keywords . list , list ) {
2023-02-13 09:24:01 -05:00
if ( strcmp ( bkwl - > scope , " SSL " ) = = 0 ) /* skip SSL keywords */
continue ;
for ( index = 0 ; bkwl - > kw [ index ] . kw ! = NULL ; index + + ) {
2022-03-30 05:21:32 -04:00
if ( strordered ( bkwp ? bkwp - > kw : NULL ,
bkwl - > kw [ index ] . kw ,
bkwn ! = bkwp ? bkwn - > kw : NULL ) )
bkwn = & bkwl - > kw [ index ] ;
2023-02-13 09:24:01 -05:00
}
2022-03-29 09:02:44 -04:00
}
2022-03-30 05:21:32 -04:00
if ( bkwn = = bkwp )
break ;
if ( ! bkwn - > skip )
printf ( " \t bind <addr> %s \n " , bkwn - > kw ) ;
else
printf ( " \t bind <addr> %s +%d \n " , bkwn - > kw , bkwn - > skip ) ;
2022-03-29 09:02:44 -04:00
}
# if defined(USE_OPENSSL)
2023-02-13 09:24:01 -05:00
/* displays the "ssl" keywords */
for ( bkwn = bkwp = NULL ; ; bkwp = bkwn ) {
list_for_each_entry ( bkwl , & bind_keywords . list , list ) {
if ( strcmp ( bkwl - > scope , " SSL " ) ! = 0 ) /* skip non-SSL keywords */
continue ;
for ( index = 0 ; bkwl - > kw [ index ] . kw ! = NULL ; index + + ) {
if ( strordered ( bkwp ? bkwp - > kw : NULL ,
bkwl - > kw [ index ] . kw ,
bkwn ! = bkwp ? bkwn - > kw : NULL ) )
bkwn = & bkwl - > kw [ index ] ;
}
2022-03-30 05:21:32 -04:00
}
2023-02-13 09:24:01 -05:00
if ( bkwn = = bkwp )
2022-03-30 05:21:32 -04:00
break ;
2023-02-13 09:24:01 -05:00
if ( strcmp ( bkwn - > kw , " ssl " ) = = 0 ) /* skip "bind <addr> ssl ssl" */
continue ;
if ( ! bkwn - > skip )
printf ( " \t bind <addr> ssl %s \n " , bkwn - > kw ) ;
2022-03-29 09:02:44 -04:00
else
2023-02-13 09:24:01 -05:00
printf ( " \t bind <addr> ssl %s +%d \n " , bkwn - > kw , bkwn - > skip ) ;
2022-03-29 09:02:44 -04:00
}
# endif
2022-03-30 05:21:32 -04:00
for ( skwn = skwp = NULL ; ; skwp = skwn ) {
list_for_each_entry ( skwl , & srv_keywords . list , list ) {
for ( index = 0 ; skwl - > kw [ index ] . kw ! = NULL ; index + + )
if ( strordered ( skwp ? skwp - > kw : NULL ,
skwl - > kw [ index ] . kw ,
skwn ! = skwp ? skwn - > kw : NULL ) )
skwn = & skwl - > kw [ index ] ;
2022-03-29 09:02:44 -04:00
}
2022-03-30 05:21:32 -04:00
if ( skwn = = skwp )
break ;
2022-03-29 09:02:44 -04:00
2022-03-30 05:21:32 -04:00
if ( ! skwn - > skip )
printf ( " \t server <name> <addr> %s \n " , skwn - > kw ) ;
else
printf ( " \t server <name> <addr> %s +%d \n " , skwn - > kw , skwn - > skip ) ;
}
for ( coptn = coptp = NULL ; ; coptp = coptn ) {
for ( index = 0 ; cfg_opts [ index ] . name ; index + + )
if ( strordered ( coptp ? coptp - > name : NULL ,
cfg_opts [ index ] . name ,
coptn ! = coptp ? coptn - > name : NULL ) )
coptn = & cfg_opts [ index ] ;
for ( index = 0 ; cfg_opts2 [ index ] . name ; index + + )
if ( strordered ( coptp ? coptp - > name : NULL ,
cfg_opts2 [ index ] . name ,
coptn ! = coptp ? coptn - > name : NULL ) )
coptn = & cfg_opts2 [ index ] ;
if ( coptn = = coptp )
break ;
2022-03-29 09:02:44 -04:00
2022-03-30 05:21:32 -04:00
printf ( " \t option %s [ " , coptn - > name ) ;
if ( coptn - > cap & PR_CAP_FE )
2022-03-29 09:02:44 -04:00
printf ( " FE " ) ;
2022-03-30 05:21:32 -04:00
if ( coptn - > cap & PR_CAP_BE )
2022-03-29 09:02:44 -04:00
printf ( " BE " ) ;
2022-03-30 05:21:32 -04:00
if ( coptn - > mode = = PR_MODE_HTTP )
2022-03-29 09:02:44 -04:00
printf ( " HTTP " ) ;
printf ( " ] \n " ) ;
}
2022-03-30 05:21:32 -04:00
dump_act_rules ( & tcp_req_conn_keywords , " \t tcp-request connection " ) ;
dump_act_rules ( & tcp_req_sess_keywords , " \t tcp-request session " ) ;
dump_act_rules ( & tcp_req_cont_keywords , " \t tcp-request content " ) ;
dump_act_rules ( & tcp_res_cont_keywords , " \t tcp-response content " ) ;
dump_act_rules ( & http_req_keywords . list , " \t http-request " ) ;
dump_act_rules ( & http_res_keywords . list , " \t http-response " ) ;
dump_act_rules ( & http_after_res_keywords . list , " \t http-after-response " ) ;
2022-03-29 09:02:44 -04:00
}
2023-06-26 14:43:48 -04:00
if ( section = = CFG_PEERS ) {
struct peers_kw_list * pkwl ;
const struct peers_keyword * pkwp , * pkwn ;
for ( pkwn = pkwp = NULL ; ; pkwp = pkwn ) {
list_for_each_entry ( pkwl , & peers_keywords . list , list ) {
for ( index = 0 ; pkwl - > kw [ index ] . kw ! = NULL ; index + + ) {
if ( strordered ( pkwp ? pkwp - > kw : NULL ,
pkwl - > kw [ index ] . kw ,
pkwn ! = pkwp ? pkwn - > kw : NULL ) )
pkwn = & pkwl - > kw [ index ] ;
}
}
if ( pkwn = = pkwp )
break ;
printf ( " \t %s \n " , pkwn - > kw ) ;
}
}
2023-02-13 09:24:01 -05:00
if ( section = = CFG_CRTLIST ) {
/* displays the keyword available for the crt-lists */
extern struct ssl_crtlist_kw ssl_crtlist_kws [ ] __maybe_unused ;
const struct ssl_crtlist_kw * sbkwp __maybe_unused , * sbkwn __maybe_unused ;
# if defined(USE_OPENSSL)
for ( sbkwn = sbkwp = NULL ; ; sbkwp = sbkwn ) {
for ( index = 0 ; ssl_crtlist_kws [ index ] . kw ! = NULL ; index + + ) {
if ( strordered ( sbkwp ? sbkwp - > kw : NULL ,
ssl_crtlist_kws [ index ] . kw ,
sbkwn ! = sbkwp ? sbkwn - > kw : NULL ) )
sbkwn = & ssl_crtlist_kws [ index ] ;
}
if ( sbkwn = = sbkwp )
break ;
if ( ! sbkwn - > skip )
printf ( " \t %s \n " , sbkwn - > kw ) ;
else
printf ( " \t %s +%d \n " , sbkwn - > kw , sbkwn - > skip ) ;
}
# endif
}
2022-03-29 09:02:44 -04:00
}
}
2018-11-26 05:33:13 -05:00
/* these are the config sections handled by default */
REGISTER_CONFIG_SECTION ( " listen " , cfg_parse_listen , NULL ) ;
REGISTER_CONFIG_SECTION ( " frontend " , cfg_parse_listen , NULL ) ;
REGISTER_CONFIG_SECTION ( " backend " , cfg_parse_listen , NULL ) ;
REGISTER_CONFIG_SECTION ( " defaults " , cfg_parse_listen , NULL ) ;
REGISTER_CONFIG_SECTION ( " global " , cfg_parse_global , NULL ) ;
REGISTER_CONFIG_SECTION ( " userlist " , cfg_parse_users , NULL ) ;
REGISTER_CONFIG_SECTION ( " mailers " , cfg_parse_mailers , NULL ) ;
REGISTER_CONFIG_SECTION ( " namespace_list " , cfg_parse_netns , NULL ) ;
2024-10-01 02:48:38 -04:00
REGISTER_CONFIG_SECTION ( " traces " , cfg_parse_traces , NULL ) ;
2016-05-26 11:55:28 -04:00
2021-04-27 14:29:11 -04:00
static struct cfg_kw_list cfg_kws = { { } , {
{ CFG_GLOBAL , " default-path " , cfg_parse_global_def_path } ,
2026-01-05 23:22:12 -05:00
{ CFG_USERLIST , " group " , cfg_parse_users_group } ,
{ CFG_USERLIST , " user " , cfg_parse_users_user } ,
2021-04-27 14:29:11 -04:00
{ /* END */ }
} } ;
INITCALL1 ( STG_REGISTER , cfg_register_keywords , & cfg_kws ) ;
2006-06-25 20:48:02 -04:00
/*
* Local variables :
* c - indent - level : 8
* c - basic - offset : 8
* End :
*/