diff -Nru haproxy-2.1.11/CHANGELOG haproxy-2.1.12/CHANGELOG --- haproxy-2.1.11/CHANGELOG 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/CHANGELOG 2021-03-18 13:25:32.000000000 +0000 @@ -1,6 +1,83 @@ ChangeLog : =========== +2021/03/18 : 2.1.12 + - BUG/MINOR: sample: check alloc_trash_chunk return value in concat() + - BUG/MINOR: sample: Memory leak of sample_expr structure in case of error + - BUG/MINOR: init: enforce strict-limits when using master-worker + - BUG/MINOR: init: Use a dynamic buffer to set HAPROXY_CFGFILES env variable + - BUG/MINOR: threads: Fixes the number of possible cpus report for Mac. + - BUG/MINOR: peers: Wrong "new_conn" value for "show peers" CLI command. + - BUG/MINOR: mux_h2: missing space between "st" and ".flg" in the "show fd" helper + - BUG/MINOR: mworker: define _GNU_SOURCE for strsignal() + - BUG/MEDIUM: mux-h2: fix read0 handling on partial frames + - BUILD/MINOR: lua: define _GNU_SOURCE for LLONG_MAX + - BUG/MEDIUM: stats: add missing INF_BUILD_INFO definition + - BUG/MEDIUM: filters/htx: Fix data forwarding when payload length is unknown + - BUG/MINOR: config: fix leak on proxy.conn_src.bind_hdr_name + - MINOR: contrib: Make the wireshark peers dissector compile for more distribs. + - DOC: management: fix "show resolvers" alphabetical ordering + - BUG/MINOR: stick-table: Always call smp_fetch_src() with a valid arg list + - BUG/MEDIUM: ssl: check a connection's status before computing a handshake + - BUG/MINOR: xxhash: make sure armv6 uses memcpy() + - BUG/MINOR: ssl: init tmp chunk correctly in ssl_sock_load_sctl_from_file() + - BUG/MEDIUM: ssl/cli: abort ssl cert is freeing the old store + - BUILD: Makefile: move REGTESTST_TYPE default setting + - BUG/MEDIUM: mux-h2: handle remaining read0 cases + - BUG/MEDIUM: mux-h2: do not quit the demux loop before setting END_REACHED + - BUG/MINOR: http-ana: Don't increment HTTP error counter on internal errors + - BUG/MEDIUM: mux-h1: Always set CS_FL_EOI for response in MSG_DONE state + - BUG/MINOR: server: re-align state file fields number + - BUG/MINOR: tools: Fix a memory leak on error path in parse_dotted_uints() + - BUG/MINOR: backend: hold correctly lock when killing idle conn + - BUG/MINOR: server: Fix server-state-file-name directive + - CLEANUP: deinit: release global and per-proxy server-state variables on deinit + - BUG/MEDIUM: config: don't pick unset values from last defaults section + - BUG/MINOR: stats: revert the change on ST_CONVDONE + - BUG/MINOR: cfgparse: do not mention "addr:port" as supported on proxy lines + - BUG/MINOR: server: Don't call fopen() with server-state filepath set to NULL + - CLEANUP: channel: fix comment in ci_putblk. + - BUG/MINOR: server: Remove RMAINT from admin state when loading server state + - BUG/MINOR: session: atomically increment the tracked sessions counter + - BUG/MINOR: checks: properly handle wrapping time in __health_adjust() + - BUG/MINOR: sample: Always consider zero size string samples as unsafe + - BUG/MINOR: server: Init params before parsing a new server-state line + - BUG/MINOR: server: Be sure to cut the last parsed field of a server-state line + - BUG/MEDIUM: mux-h1: Fix handling of responses to CONNECT other than 200-ok + - BUG/MINOR: ssl/cli: potential null pointer dereference in "set ssl cert" + - BUG/MINOR: sample: secure convs that accept base64 string and var name as args + - BUG/MEDIUM: vars: make functions vars_get_by_{name,desc} thread-safe + - BUG/MEDIUM: proxy: use thread-safe stream killing on hard-stop + - BUG/MEDIUM: cli/shutdown sessions: make it thread-safe + - BUG/MINOR: proxy: wake up all threads when sending the hard-stop signal + - BUG/MINOR: resolvers: new callback to properly handle SRV record errors + - BUG/MEDIUM: resolvers: Reset server address and port for obselete SRV records + - BUG/MEDIUM: resolvers: Reset address for unresolved servers + - BUG/MINOR: mux-h1: Immediately report H1C errors from h1_snd_buf() + - BUG/MINOR: http-ana: Only consider dst address to process originalto option + - BUG/MINOR: tcp-act: Don't forget to set the original port for IPv4 set-dst rule + - BUG/MINOR: connection: Use the client's dst family for adressless servers + - BUG/MEDIUM: spoe: Kill applets if there are pending connections and nbthread > 1 + - DOC: spoe: Add a note about fragmentation support in HAProxy + - BUG/MINOR: mux-h2: Fix typo in scheme adjustment + - BUG/MINOR: http-ana: Don't increment HTTP error counter on read error/timeout + - BUG/MEDIUM: dns: Consider the fact that dns answers are case-insensitive + - BUG/MEDIUM: lists: Lock the element while we check if it is in a list. + - BUG/MEDIUM: lists: Avoid an infinite loop in MT_LIST_TRY_ADDQ(). + - BUG/MINOR: hlua: Don't strip last non-LWS char in hlua_pushstrippedstring() + - BUG/MINOR: ssl: don't truncate the file descriptor to 16 bits in debug mode + - BUG/MEDIUM: session: NULL dereference possible when accessing the listener + - BUG/MEDIUM: filters: Set CF_FL_ANALYZE on channels when filters are attached + - BUG/MINOR: proxy/session: Be sure to have a listener to increment its counters + - BUG/MINOR: session: Add some forgotten tests on session's listener + - CLEANUP: tcp-rules: add missing actions in the tcp-request error message + - BUG/MINOR: resolvers: Consider server to have no IP on DNS resolution error + - BUG/MINOR: resolvers: Reset server address on DNS error only on status change + - BUG/MINOR: resolvers: Add missing case-insensitive comparisons of DNS hostnames + - MINOR: time: export the global_now variable + - BUG/MINOR: freq_ctr/threads: make use of the last updated global time + - MINOR: version: Set the EOL of the 2.1 branch + 2021/01/08 : 2.1.11 - MINOR: http-htx: Add understandable errors for the errorfiles parsing - BUG/MINOR: http-htx: Just warn if payload of an errorfile doesn't match the C-L diff -Nru haproxy-2.1.11/contrib/wireshark-dissectors/peers/Makefile haproxy-2.1.12/contrib/wireshark-dissectors/peers/Makefile --- haproxy-2.1.11/contrib/wireshark-dissectors/peers/Makefile 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/contrib/wireshark-dissectors/peers/Makefile 2021-03-18 13:25:32.000000000 +0000 @@ -1,4 +1,4 @@ -CFLAGS = `pkg-config --cflags wireshark` -g -fPIC +CFLAGS = `pkg-config --cflags wireshark` -g -fPIC $(OPTS) LDFLAGS = `pkg-config --libs wireshark` NAME = packet-happp.so diff -Nru haproxy-2.1.11/contrib/wireshark-dissectors/peers/packet-happp.c haproxy-2.1.12/contrib/wireshark-dissectors/peers/packet-happp.c --- haproxy-2.1.11/contrib/wireshark-dissectors/peers/packet-happp.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/contrib/wireshark-dissectors/peers/packet-happp.c 2021-03-18 13:25:32.000000000 +0000 @@ -36,9 +36,25 @@ #include #include +#ifndef WITHOUT_WS_VERSION #include +#endif + +#ifndef WIRESHARK_VERSION_MAJOR +#define WIRESHARK_VERSION_MAJOR VERSION_MAJOR +#endif +#ifndef WIRESHARK_VERSION_MINOR +#define WIRESHARK_VERSION_MINOR VERSION_MINOR +#endif +#ifndef WIRESHARK_VERSION_MICRO +#define WIRESHARK_VERSION_MICRO VERSION_MICRO +#endif + +#define HAPP_STR(str) #str +#define HAPP_XSTR(str) HAPP_STR(str) WS_DLL_PUBLIC_DEF const gchar plugin_version[] = "0.0.1"; +WS_DLL_PUBLIC_DEF const gchar plugin_release[] = HAPP_XSTR(WIRESHARK_VERSION_MAJOR.WIRESHARK_VERSION_MINOR); WS_DLL_PUBLIC_DEF const int plugin_want_major = WIRESHARK_VERSION_MAJOR; WS_DLL_PUBLIC_DEF const int plugin_want_minor = WIRESHARK_VERSION_MINOR; WS_DLL_PUBLIC void plugin_register(void); diff -Nru haproxy-2.1.11/contrib/wireshark-dissectors/peers/README haproxy-2.1.12/contrib/wireshark-dissectors/peers/README --- haproxy-2.1.11/contrib/wireshark-dissectors/peers/README 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/contrib/wireshark-dissectors/peers/README 2021-03-18 13:25:32.000000000 +0000 @@ -37,6 +37,16 @@ $ make +It is possible that depending on your distribution the compilation may fail +with such an error: + + packet-happp.c:40:10: fatal error: ws_version.h: No such file or directory + #include + +In this case try to build this plugins with this OPTS variable: + +$ OPTS=-DWITHOUT_WS_VERSION make + To install it in your home directory: $ make install diff -Nru haproxy-2.1.11/debian/changelog haproxy-2.1.12/debian/changelog --- haproxy-2.1.11/debian/changelog 2021-01-10 19:45:11.000000000 +0000 +++ haproxy-2.1.12/debian/changelog 2021-03-20 17:40:45.000000000 +0000 @@ -1,3 +1,15 @@ +haproxy (2.1.12-1ppa1~bionic) bionic; urgency=medium + + * Rebuild for bionic. + + -- Vincent Bernat Sat, 20 Mar 2021 18:40:45 +0100 + +haproxy (2.1.12-1) UNRELEASED; urgency=medium + + * New upstream version. + + -- Vincent Bernat Sat, 20 Mar 2021 17:58:06 +0100 + haproxy (2.1.11-1ppa1~bionic) bionic; urgency=medium * Backport for Bionic (PPA). diff -Nru haproxy-2.1.11/doc/configuration.txt haproxy-2.1.12/doc/configuration.txt --- haproxy-2.1.11/doc/configuration.txt 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/doc/configuration.txt 2021-03-18 13:25:32.000000000 +0000 @@ -4,7 +4,7 @@ ---------------------- version 2.1 willy tarreau - 2021/01/08 + 2021/03/18 This document covers the configuration language as implemented in the version @@ -8044,13 +8044,17 @@ See also: "default-server", "http-send-name-header" and section 5 about server options -server-state-file-name [] +server-state-file-name [ { use-backend-name | } ] Set the server state file to read, load and apply to servers available in - this backend. It only applies when the directive "load-server-state-from-file" - is set to "local". When is not provided or if this directive is not - set, then backend name is used. If starts with a slash '/', then it is - considered as an absolute path. Otherwise, is concatenated to the - global directive "server-state-file-base". + this backend. + May be used in sections: defaults | frontend | listen | backend + no | no | yes | yes + + It only applies when the directive "load-server-state-from-file" is set to + "local". When is not provided, if "use-backend-name" is used or if + this directive is not set, then backend name is used. If starts with a + slash '/', then it is considered as an absolute path. Otherwise, is + concatenated to the global directive "server-state-base". Example: the minimal configuration below would make HAProxy look for the state server file '/etc/haproxy/states/bk': @@ -8061,7 +8065,7 @@ backend bk load-server-state-from-file - See also: "server-state-file-base", "load-server-state-from-file", and + See also: "server-state-base", "load-server-state-from-file", and "show servers state" server-template [:] [params*] diff -Nru haproxy-2.1.11/doc/management.txt haproxy-2.1.12/doc/management.txt --- haproxy-2.1.11/doc/management.txt 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/doc/management.txt 2021-03-18 13:25:32.000000000 +0000 @@ -2234,6 +2234,25 @@ Dumps the current profiling settings, one per line, as well as the command needed to change them. +show resolvers [] + Dump statistics for the given resolvers section, or all resolvers sections + if no section is supplied. + + For each name server, the following counters are reported: + sent: number of DNS requests sent to this server + valid: number of DNS valid responses received from this server + update: number of DNS responses used to update the server's IP address + cname: number of CNAME responses + cname_error: CNAME errors encountered with this server + any_err: number of empty response (IE: server does not support ANY type) + nx: non existent domain response received from this server + timeout: how many time this server did not answer in time + refused: number of requests refused by this server + other: any other DNS errors + invalid: invalid DNS response (from a protocol point of view) + too_big: too big response + outdated: number of response arrived too late (after an other name server) + show servers state [] Dump the state of the servers found in the running configuration. A backend name or identifier may be provided to limit the output to this backend only. @@ -2511,25 +2530,6 @@ $ echo "show stat json" | socat /var/run/haproxy.sock stdio | \ python -m json.tool -show resolvers [] - Dump statistics for the given resolvers section, or all resolvers sections - if no section is supplied. - - For each name server, the following counters are reported: - sent: number of DNS requests sent to this server - valid: number of DNS valid responses received from this server - update: number of DNS responses used to update the server's IP address - cname: number of CNAME responses - cname_error: CNAME errors encountered with this server - any_err: number of empty response (IE: server does not support ANY type) - nx: non existent domain response received from this server - timeout: how many time this server did not answer in time - refused: number of requests refused by this server - other: any other DNS errors - invalid: invalid DNS response (from a protocol point of view) - too_big: too big response - outdated: number of response arrived too late (after an other name server) - show table Dump general information on all known stick-tables. Their name is returned (the name of the proxy which holds them), their type (currently zero, always diff -Nru haproxy-2.1.11/doc/SPOE.txt haproxy-2.1.12/doc/SPOE.txt --- haproxy-2.1.11/doc/SPOE.txt 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/doc/SPOE.txt 2021-03-18 13:25:32.000000000 +0000 @@ -761,6 +761,10 @@ Unsupported or unknown capabilities are silently ignored, when possible. +NOTE: HAProxy does not support the fragmentation for now. This means it is not + able to handle fragmented frames. However, if an agent announces the + fragmentation support, HAProxy may choose to send fragemented frames. + 3.2.2. Frame types overview ---------------------------- diff -Nru haproxy-2.1.11/include/common/mini-clist.h haproxy-2.1.12/include/common/mini-clist.h --- haproxy-2.1.11/include/common/mini-clist.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/common/mini-clist.h 2021-03-18 13:25:32.000000000 +0000 @@ -229,36 +229,53 @@ ({ \ int _ret = 0; \ struct mt_list *lh = (_lh), *el = (_el); \ - do { \ - while (1) { \ - struct mt_list *n; \ - struct mt_list *p; \ - n = _HA_ATOMIC_XCHG(&(lh)->next, MT_LIST_BUSY); \ - if (n == MT_LIST_BUSY) \ - continue; \ - p = _HA_ATOMIC_XCHG(&n->prev, MT_LIST_BUSY); \ - if (p == MT_LIST_BUSY) { \ - (lh)->next = n; \ - __ha_barrier_store(); \ - continue; \ - } \ - if ((el)->next != (el) || (el)->prev != (el)) { \ - (n)->prev = p; \ - (lh)->next = n; \ - __ha_barrier_store(); \ - break; \ - } \ - (el)->next = n; \ - (el)->prev = p; \ + while (1) { \ + struct mt_list *n, *n2; \ + struct mt_list *p, *p2; \ + n = _HA_ATOMIC_XCHG(&(lh)->next, MT_LIST_BUSY); \ + if (n == MT_LIST_BUSY) \ + continue; \ + p = _HA_ATOMIC_XCHG(&n->prev, MT_LIST_BUSY); \ + if (p == MT_LIST_BUSY) { \ + (lh)->next = n; \ __ha_barrier_store(); \ - n->prev = (el); \ + continue; \ + } \ + n2 = _HA_ATOMIC_XCHG(&el->next, MT_LIST_BUSY); \ + if (n2 != el) { /* element already linked */ \ + if (n2 != MT_LIST_BUSY) \ + el->next = n2; \ + n->prev = p; \ __ha_barrier_store(); \ - p->next = (el); \ + lh->next = n; \ __ha_barrier_store(); \ - _ret = 1; \ + if (n2 == MT_LIST_BUSY) \ + continue; \ break; \ } \ - } while (0); \ + p2 = _HA_ATOMIC_XCHG(&el->prev, MT_LIST_BUSY); \ + if (p2 != el) { \ + if (p2 != MT_LIST_BUSY) \ + el->prev = p2; \ + n->prev = p; \ + el->next = el; \ + __ha_barrier_store(); \ + lh->next = n; \ + __ha_barrier_store(); \ + if (p2 == MT_LIST_BUSY) \ + continue; \ + break; \ + } \ + (el)->next = n; \ + (el)->prev = p; \ + __ha_barrier_store(); \ + n->prev = (el); \ + __ha_barrier_store(); \ + p->next = (el); \ + __ha_barrier_store(); \ + _ret = 1; \ + break; \ + } \ (_ret); \ }) @@ -269,38 +286,55 @@ */ #define MT_LIST_ADDQ(_lh, _el) \ ({ \ - int _ret = 0; \ - struct mt_list *lh = (_lh), *el = (_el); \ - do { \ - while (1) { \ - struct mt_list *n; \ - struct mt_list *p; \ - p = _HA_ATOMIC_XCHG(&(lh)->prev, MT_LIST_BUSY); \ - if (p == MT_LIST_BUSY) \ - continue; \ - n = _HA_ATOMIC_XCHG(&p->next, MT_LIST_BUSY); \ - if (n == MT_LIST_BUSY) { \ - (lh)->prev = p; \ - __ha_barrier_store(); \ - continue; \ - } \ - if ((el)->next != (el) || (el)->prev != (el)) { \ - p->next = n; \ - (lh)->prev = p; \ - __ha_barrier_store(); \ - break; \ - } \ - (el)->next = n; \ - (el)->prev = p; \ + int _ret = 0; \ + struct mt_list *lh = (_lh), *el = (_el); \ + while (1) { \ + struct mt_list *n, *n2; \ + struct mt_list *p, *p2; \ + p = _HA_ATOMIC_XCHG(&(lh)->prev, MT_LIST_BUSY); \ + if (p == MT_LIST_BUSY) \ + continue; \ + n = _HA_ATOMIC_XCHG(&p->next, MT_LIST_BUSY); \ + if (n == MT_LIST_BUSY) { \ + (lh)->prev = p; \ + __ha_barrier_store(); \ + continue; \ + } \ + p2 = _HA_ATOMIC_XCHG(&el->prev, MT_LIST_BUSY); \ + if (p2 != el) { \ + if (p2 != MT_LIST_BUSY) \ + el->prev = p2; \ + p->next = n; \ + __ha_barrier_store(); \ + lh->prev = p; \ __ha_barrier_store(); \ - p->next = (el); \ + if (p2 == MT_LIST_BUSY) \ + continue; \ + break; \ + } \ + n2 = _HA_ATOMIC_XCHG(&el->next, MT_LIST_BUSY); \ + if (n2 != el) { /* element already linked */ \ + if (n2 != MT_LIST_BUSY) \ + el->next = n2; \ + p->next = n; \ + el->prev = el; \ __ha_barrier_store(); \ - n->prev = (el); \ + lh->prev = p; \ __ha_barrier_store(); \ - _ret = 1; \ + if (n2 == MT_LIST_BUSY) \ + continue; \ break; \ } \ - } while (0); \ + (el)->next = n; \ + (el)->prev = p; \ + __ha_barrier_store(); \ + p->next = (el); \ + __ha_barrier_store(); \ + n->prev = (el); \ + __ha_barrier_store(); \ + _ret = 1; \ + break; \ + } \ (_ret); \ }) diff -Nru haproxy-2.1.11/include/common/time.h haproxy-2.1.12/include/common/time.h --- haproxy-2.1.11/include/common/time.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/common/time.h 2021-03-18 13:25:32.000000000 +0000 @@ -63,6 +63,7 @@ extern struct timeval start_date; /* the process's start date */ extern THREAD_LOCAL struct timeval before_poll; /* system date before calling poll() */ extern THREAD_LOCAL struct timeval after_poll; /* system date after leaving poll() */ +extern volatile unsigned long long global_now; /**** exported functions *************************************************/ diff -Nru haproxy-2.1.11/include/common/version.h haproxy-2.1.12/include/common/version.h --- haproxy-2.1.11/include/common/version.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/common/version.h 2021-03-18 13:25:32.000000000 +0000 @@ -39,7 +39,7 @@ #ifdef CONFIG_PRODUCT_STATUS #define PRODUCT_STATUS CONFIG_PRODUCT_STATUS #else -#define PRODUCT_STATUS "Status: stable branch - will stop receiving fixes around Q1 2021." +#define PRODUCT_STATUS "Status: End of life - please upgrade to branch 2.2." #endif #ifdef CONFIG_PRODUCT_URL_BUGS diff -Nru haproxy-2.1.11/include/proto/freq_ctr.h haproxy-2.1.12/include/proto/freq_ctr.h --- haproxy-2.1.11/include/proto/freq_ctr.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/proto/freq_ctr.h 2021-03-18 13:25:32.000000000 +0000 @@ -36,6 +36,7 @@ { int elapsed; unsigned int curr_sec; + uint32_t now_tmp; /* we manipulate curr_ctr using atomic ops out of the lock, since @@ -47,16 +48,17 @@ * same uncertainty as well. */ curr_sec = ctr->curr_sec; - if (curr_sec == (now.tv_sec & 0x7fffffff)) - return _HA_ATOMIC_ADD(&ctr->curr_ctr, inc); - do { + now_tmp = global_now >> 32; + if (curr_sec == (now_tmp & 0x7fffffff)) + return _HA_ATOMIC_ADD(&ctr->curr_ctr, inc); + /* remove the bit, used for the lock */ curr_sec &= 0x7fffffff; } while (!_HA_ATOMIC_CAS(&ctr->curr_sec, &curr_sec, curr_sec | 0x80000000)); __ha_barrier_atomic_store(); - elapsed = (now.tv_sec & 0x7fffffff)- curr_sec; + elapsed = (now_tmp & 0x7fffffff) - curr_sec; if (unlikely(elapsed > 0)) { ctr->prev_ctr = ctr->curr_ctr; _HA_ATOMIC_SUB(&ctr->curr_ctr, ctr->prev_ctr); @@ -64,7 +66,7 @@ /* we missed more than one second */ ctr->prev_ctr = 0; } - curr_sec = now.tv_sec; + curr_sec = now_tmp; } /* release the lock and update the time in case of rotate. */ @@ -82,25 +84,27 @@ unsigned int period, unsigned int inc) { unsigned int curr_tick; + uint32_t now_ms_tmp; curr_tick = ctr->curr_tick; - if (now_ms - curr_tick < period) - return _HA_ATOMIC_ADD(&ctr->curr_ctr, inc); - do { + now_ms_tmp = (uint32_t)global_now / 1000; + if (now_ms_tmp - curr_tick < period) + return _HA_ATOMIC_ADD(&ctr->curr_ctr, inc); + /* remove the bit, used for the lock */ curr_tick &= ~1; } while (!_HA_ATOMIC_CAS(&ctr->curr_tick, &curr_tick, curr_tick | 0x1)); __ha_barrier_atomic_store(); - if (now_ms - curr_tick >= period) { + if (now_ms_tmp - curr_tick >= period) { ctr->prev_ctr = ctr->curr_ctr; _HA_ATOMIC_SUB(&ctr->curr_ctr, ctr->prev_ctr); curr_tick += period; - if (likely(now_ms - curr_tick >= period)) { + if (likely(now_ms_tmp - curr_tick >= period)) { /* we missed at least two periods */ ctr->prev_ctr = 0; - curr_tick = now_ms; + curr_tick = now_ms_tmp; } curr_tick &= ~1; } diff -Nru haproxy-2.1.11/include/proto/proxy.h haproxy-2.1.12/include/proto/proxy.h --- haproxy-2.1.11/include/proto/proxy.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/proto/proxy.h 2021-03-18 13:25:32.000000000 +0000 @@ -114,7 +114,7 @@ static inline void proxy_inc_fe_conn_ctr(struct listener *l, struct proxy *fe) { _HA_ATOMIC_ADD(&fe->fe_counters.cum_conn, 1); - if (l->counters) + if (l && l->counters) _HA_ATOMIC_ADD(&l->counters->cum_conn, 1); HA_ATOMIC_UPDATE_MAX(&fe->fe_counters.cps_max, update_freq_ctr(&fe->fe_conn_per_sec, 1)); @@ -125,7 +125,7 @@ { _HA_ATOMIC_ADD(&fe->fe_counters.cum_sess, 1); - if (l->counters) + if (l && l->counters) _HA_ATOMIC_ADD(&l->counters->cum_sess, 1); HA_ATOMIC_UPDATE_MAX(&fe->fe_counters.sps_max, update_freq_ctr(&fe->fe_sess_per_sec, 1)); diff -Nru haproxy-2.1.11/include/proto/sample.h haproxy-2.1.12/include/proto/sample.h --- haproxy-2.1.11/include/proto/sample.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/proto/sample.h 2021-03-18 13:25:32.000000000 +0000 @@ -93,13 +93,13 @@ /* Fall through */ case SMP_T_STR: - if (smp->data.u.str.size && smp->data.u.str.data >= smp->data.u.str.size) + if (!smp->data.u.str.size || smp->data.u.str.data >= smp->data.u.str.size) return 0; if (smp->data.u.str.area[smp->data.u.str.data] == 0) return 1; - if (!smp->data.u.str.size || (smp->flags & SMP_F_CONST)) + if (smp->flags & SMP_F_CONST) return 0; smp->data.u.str.area[smp->data.u.str.data] = 0; diff -Nru haproxy-2.1.11/include/proto/server.h haproxy-2.1.12/include/proto/server.h --- haproxy-2.1.11/include/proto/server.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/proto/server.h 2021-03-18 13:25:32.000000000 +0000 @@ -63,6 +63,7 @@ int snr_update_srv_status(struct server *s, int has_no_ip); const char *update_server_fqdn(struct server *server, const char *fqdn, const char *updater, int dns_locked); int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver *nameserver); +int srvrq_resolution_error_cb(struct dns_requester *requester, int error_code); int snr_resolution_error_cb(struct dns_requester *requester, int error_code); struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned char *ip_family); struct task *srv_cleanup_idle_connections(struct task *task, void *ctx, unsigned short state); diff -Nru haproxy-2.1.11/include/types/filters.h haproxy-2.1.12/include/types/filters.h --- haproxy-2.1.11/include/types/filters.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/types/filters.h 2021-03-18 13:25:32.000000000 +0000 @@ -199,6 +199,7 @@ /* Flags set on the stream, common to all filters attached to its stream */ #define STRM_FLT_FL_HAS_FILTERS 0x0001 /* The stream has at least one filter */ +#define STRM_FLT_FL_HOLD_HTTP_HDRS 0x0002 /* At least one filter on the stream want to hold the message headers */ /* * Structure representing the filter configuration, attached to a proxy and diff -Nru haproxy-2.1.11/include/types/server.h haproxy-2.1.12/include/types/server.h --- haproxy-2.1.11/include/types/server.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/types/server.h 2021-03-18 13:25:32.000000000 +0000 @@ -127,7 +127,7 @@ "srvrecord" #define SRV_STATE_FILE_MAX_FIELDS 20 -#define SRV_STATE_FILE_NB_FIELDS_VERSION_1 19 +#define SRV_STATE_FILE_NB_FIELDS_VERSION_1 20 #define SRV_STATE_LINE_MAXLEN 512 /* server flags -- 32 bits */ diff -Nru haproxy-2.1.11/include/types/stats.h haproxy-2.1.12/include/types/stats.h --- haproxy-2.1.11/include/types/stats.h 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/include/types/stats.h 2021-03-18 13:25:32.000000000 +0000 @@ -35,6 +35,7 @@ #define STAT_SHDESC 0x00000400 /* conf: show description */ #define STAT_SHLGNDS 0x00000800 /* conf: show legends */ #define STAT_SHOW_FDESC 0x00001000 /* show the field descriptions when possible */ +#define STAT_CONVDONE 0x00008000 /* conf: rules conversion done */ #define STAT_BOUND 0x00800000 /* bound statistics to selected proxies/types/services */ #define STAT_STARTED 0x01000000 /* some output has occurred */ diff -Nru haproxy-2.1.11/Makefile haproxy-2.1.12/Makefile --- haproxy-2.1.11/Makefile 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/Makefile 2021-03-18 13:25:32.000000000 +0000 @@ -997,7 +997,6 @@ @echo 'OBJS="$(strip $(OBJS))"' ifeq (reg-tests, $(firstword $(MAKECMDGOALS))) - REGTESTS_TYPES := default,bug,devel,slow REGTEST_ARGS := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS)) $(eval $(REGTEST_ARGS):;@true) endif @@ -1027,7 +1026,7 @@ @echo "To run tests with specific types:" @echo " $$ REGTESTS_TYPES=slow,default make reg-tests" @echo - @echo "with 'any' as default value for REGTESTS_TYPES variable." + @echo "with 'default,bug,devel,slow' as default value for REGTESTS_TYPES variable." @echo @echo "About the reg test types:" @echo " any : all the tests without distinction (this is the default" diff -Nru haproxy-2.1.11/scripts/run-regtests.sh haproxy-2.1.12/scripts/run-regtests.sh --- haproxy-2.1.11/scripts/run-regtests.sh 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/scripts/run-regtests.sh 2021-03-18 13:25:32.000000000 +0000 @@ -123,7 +123,7 @@ _findtests() { set -f - REGTESTS_TYPES="${REGTESTS_TYPES:-any}" + REGTESTS_TYPES="${REGTESTS_TYPES:-default,bug,devel,slow}" any_test=$(echo $REGTESTS_TYPES | grep -cw "any") for i in $( find "$1" -name *.vtc ); do skiptest= diff -Nru haproxy-2.1.11/src/backend.c haproxy-2.1.12/src/backend.c --- haproxy-2.1.11/src/backend.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/backend.c 2021-03-18 13:25:32.000000000 +0000 @@ -852,8 +852,10 @@ if (!conn_get_dst(cli_conn)) { /* do nothing if we can't retrieve the address */ } else if (cli_conn->dst->ss_family == AF_INET) { + ((struct sockaddr_in *)s->target_addr)->sin_family = AF_INET; ((struct sockaddr_in *)s->target_addr)->sin_addr = ((struct sockaddr_in *)cli_conn->dst)->sin_addr; } else if (cli_conn->dst->ss_family == AF_INET6) { + ((struct sockaddr_in6 *)s->target_addr)->sin6_family = AF_INET6; ((struct sockaddr_in6 *)s->target_addr)->sin6_addr = ((struct sockaddr_in6 *)cli_conn->dst)->sin6_addr; } } @@ -1303,7 +1305,7 @@ // see it possibly larger. ALREADY_CHECKED(i); - HA_SPIN_LOCK(OTHER_LOCK, &toremove_lock[tid]); + HA_SPIN_LOCK(OTHER_LOCK, &toremove_lock[i]); tokill_conn = MT_LIST_POP(&srv->idle_orphan_conns[i], struct connection *, list); if (tokill_conn) { @@ -1312,10 +1314,10 @@ MT_LIST_ADDQ(&toremove_connections[i], (struct mt_list *)&tokill_conn->list); task_wakeup(idle_conn_cleanup[i], TASK_WOKEN_OTHER); - HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]); + HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[i]); break; } - HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]); + HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[i]); } } diff -Nru haproxy-2.1.11/src/cfgparse.c haproxy-2.1.12/src/cfgparse.c --- haproxy-2.1.11/src/cfgparse.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/cfgparse.c 2021-03-18 13:25:32.000000000 +0000 @@ -2913,7 +2913,7 @@ } } - if (curproxy->uri_auth && curproxy->uri_auth != defproxy.uri_auth && + if (curproxy->uri_auth && !(curproxy->uri_auth->flags & STAT_CONVDONE) && !LIST_ISEMPTY(&curproxy->uri_auth->http_req_rules) && (curproxy->uri_auth->userlist || curproxy->uri_auth->auth_realm )) { ha_alert("%s '%s': stats 'auth'/'realm' and 'http-request' can't be used at the same time.\n", @@ -2923,7 +2923,7 @@ } if (curproxy->uri_auth && curproxy->uri_auth->userlist && - (curproxy->uri_auth != defproxy.uri_auth || + (!(curproxy->uri_auth->flags & STAT_CONVDONE) || LIST_ISEMPTY(&curproxy->uri_auth->http_req_rules))) { const char *uri_auth_compat_req[10]; struct act_rule *rule; @@ -2954,6 +2954,7 @@ free(curproxy->uri_auth->auth_realm); curproxy->uri_auth->auth_realm = NULL; } + curproxy->uri_auth->flags |= STAT_CONVDONE; } out_uri_auth_compat: @@ -3064,33 +3065,10 @@ * We must still support older configurations, so let's find out whether those * parameters have been set or must be copied from contimeouts. */ - if (curproxy != &defproxy) { - if (!curproxy->timeout.tarpit || - curproxy->timeout.tarpit == defproxy.timeout.tarpit) { - /* tarpit timeout not set. We search in the following order: - * default.tarpit, curr.connect, default.connect. - */ - if (defproxy.timeout.tarpit) - curproxy->timeout.tarpit = defproxy.timeout.tarpit; - else if (curproxy->timeout.connect) - curproxy->timeout.tarpit = curproxy->timeout.connect; - else if (defproxy.timeout.connect) - curproxy->timeout.tarpit = defproxy.timeout.connect; - } - if ((curproxy->cap & PR_CAP_BE) && - (!curproxy->timeout.queue || - curproxy->timeout.queue == defproxy.timeout.queue)) { - /* queue timeout not set. We search in the following order: - * default.queue, curr.connect, default.connect. - */ - if (defproxy.timeout.queue) - curproxy->timeout.queue = defproxy.timeout.queue; - else if (curproxy->timeout.connect) - curproxy->timeout.queue = curproxy->timeout.connect; - else if (defproxy.timeout.connect) - curproxy->timeout.queue = defproxy.timeout.connect; - } - } + if (!curproxy->timeout.tarpit) + curproxy->timeout.tarpit = curproxy->timeout.connect; + if ((curproxy->cap & PR_CAP_BE) && !curproxy->timeout.queue) + curproxy->timeout.queue = curproxy->timeout.connect; if ((curproxy->options2 & PR_O2_CHK_ANY) == PR_O2_SSL3_CHK) { curproxy->check_len = sizeof(sslv3_client_hello_pkt) - 1; diff -Nru haproxy-2.1.11/src/cfgparse-listen.c haproxy-2.1.12/src/cfgparse-listen.c --- haproxy-2.1.11/src/cfgparse-listen.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/cfgparse-listen.c 2021-03-18 13:25:32.000000000 +0000 @@ -187,8 +187,7 @@ if (rc != PR_CAP_NONE) { /* new proxy */ if (!*args[1]) { - ha_alert("parsing [%s:%d] : '%s' expects an argument and\n" - " optionally supports [addr1]:port1[-end1]{,[addr]:port[-end]}...\n", + ha_alert("parsing [%s:%d] : '%s' expects an argument\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_ABORT; goto out; @@ -1259,13 +1258,13 @@ else if (!strcmp(args[0], "server-state-file-name")) { if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) err_code |= ERR_WARN; - if (*(args[1]) == 0) { - ha_alert("parsing [%s:%d] : '%s' expects 'use-backend-name' or a string. Got no argument\n", - file, linenum, args[0]); - err_code |= ERR_ALERT | ERR_FATAL; + if (alertif_too_many_args(1, file, linenum, args, &err_code)) goto out; - } - else if (!strcmp(args[1], "use-backend-name")) + + free(curproxy->server_state_file_name); + curproxy->server_state_file_name = NULL; + + if (*(args[1]) == 0 || strcmp(args[1], "use-backend-name") == 0) curproxy->server_state_file_name = strdup(curproxy->id); else curproxy->server_state_file_name = strdup(args[1]); @@ -3754,6 +3753,7 @@ curproxy->conn_src.opts &= ~CO_SRC_TPROXY_MASK; curproxy->conn_src.opts |= CO_SRC_TPROXY_DYN; + free(curproxy->conn_src.bind_hdr_name); curproxy->conn_src.bind_hdr_name = calloc(1, end - name + 1); curproxy->conn_src.bind_hdr_len = end - name; memcpy(curproxy->conn_src.bind_hdr_name, name, end - name); diff -Nru haproxy-2.1.11/src/channel.c haproxy-2.1.12/src/channel.c --- haproxy-2.1.11/src/channel.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/channel.c 2021-03-18 13:25:32.000000000 +0000 @@ -153,8 +153,9 @@ max = channel_recv_limit(chn); if (unlikely(len > max - c_data(chn))) { /* we can't write this chunk right now because the buffer is - * almost full or because the block is too large. Return the - * available space or -2 if impossible. + * almost full or because the block is too large. Returns + * -3 if block is too large for this buffer. Or -1 if the + * room left is not large enough. */ if (len > max) return -3; diff -Nru haproxy-2.1.11/src/checks.c haproxy-2.1.12/src/checks.c --- haproxy-2.1.11/src/checks.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/checks.c 2021-03-18 13:25:32.000000000 +0000 @@ -473,7 +473,7 @@ if (s->check.fastinter) { expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter)); - if (s->check.task->expire > expire) { + if (tick_is_lt(expire, s->check.task->expire)) { s->check.task->expire = expire; /* requeue check task with new expire */ task_queue(s->check.task); diff -Nru haproxy-2.1.11/src/dns.c haproxy-2.1.12/src/dns.c --- haproxy-2.1.11/src/dns.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/dns.c 2021-03-18 13:25:32.000000000 +0000 @@ -77,6 +77,19 @@ return NULL; } +/* Compare hostnames in a case-insensitive way . + * Returns 0 if they are the same, non-zero otherwise + */ +static __inline int dns_hostname_cmp(const char *name1, const char *name2, int len) +{ + int i; + + for (i = 0; i < len; i++) + if (tolower(name1[i]) != tolower(name2[i])) + return -1; + return 0; +} + /* Returns a pointer on the SRV request matching the name for the proxy * . NULL is returned if no match is found. */ @@ -505,13 +518,15 @@ HA_SPIN_LOCK(SERVER_LOCK, &srv->lock); if (srv->srvrq == srvrq && srv->svc_port == item->port && item->data_len == srv->hostname_dn_len && - !memcmp(srv->hostname_dn, item->target, item->data_len)) { + !dns_hostname_cmp(srv->hostname_dn, item->target, item->data_len)) { snr_update_srv_status(srv, 1); free(srv->hostname); free(srv->hostname_dn); srv->hostname = NULL; srv->hostname_dn = NULL; srv->hostname_dn_len = 0; + memset(&srv->addr, 0, sizeof(srv->addr)); + srv->svc_port = 0; dns_unlink_resolution(srv->dns_requester); } HA_SPIN_UNLOCK(SERVER_LOCK, &srv->lock); @@ -537,7 +552,7 @@ HA_SPIN_LOCK(SERVER_LOCK, &srv->lock); if (srv->srvrq == srvrq && srv->svc_port == item->port && item->data_len == srv->hostname_dn_len && - !memcmp(srv->hostname_dn, item->target, item->data_len) && + !dns_hostname_cmp(srv->hostname_dn, item->target, item->data_len) && !srv->dns_opts.ignore_weight) { int ha_weight; @@ -763,7 +778,7 @@ /* Check if the current record dname is valid. previous_dname * points either to queried dname or last CNAME target */ - if (dns_query->type != DNS_RTYPE_SRV && memcmp(previous_dname, tmpname, len) != 0) { + if (dns_query->type != DNS_RTYPE_SRV && dns_hostname_cmp(previous_dname, tmpname, len) != 0) { pool_free(dns_answer_item_pool, dns_answer_record); if (i == 0) { /* First record, means a mismatch issue between @@ -942,7 +957,7 @@ case DNS_RTYPE_SRV: if (dns_answer_record->data_len == tmp_record->data_len && - !memcmp(dns_answer_record->target, tmp_record->target, dns_answer_record->data_len) && + !dns_hostname_cmp(dns_answer_record->target, tmp_record->target, dns_answer_record->data_len) && dns_answer_record->port == tmp_record->port) { tmp_record->weight = dns_answer_record->weight; found = 1; @@ -1294,7 +1309,7 @@ continue; if ((query_type == res->prefered_query_type) && hostname_dn_len == res->hostname_dn_len && - !memcmp(*hostname_dn, res->hostname_dn, hostname_dn_len)) + !dns_hostname_cmp(*hostname_dn, res->hostname_dn, hostname_dn_len)) return res; } @@ -1304,7 +1319,7 @@ continue; if ((query_type == res->prefered_query_type) && hostname_dn_len == res->hostname_dn_len && - !memcmp(*hostname_dn, res->hostname_dn, hostname_dn_len)) + !dns_hostname_cmp(*hostname_dn, res->hostname_dn, hostname_dn_len)) return res; } @@ -1441,7 +1456,7 @@ req = srvrq->dns_requester; req->requester_cb = snr_resolution_cb; - req->requester_error_cb = snr_resolution_error_cb; + req->requester_error_cb = srvrq_resolution_error_cb; } else if (stream) { if (stream->dns_ctx.dns_requester == NULL) { @@ -1681,7 +1696,7 @@ * sent. We can check only the first query of the list. We send * one query at a time so we get one query in the response */ query = LIST_NEXT(&res->response.query_list, struct dns_query_item *, list); - if (query && memcmp(query->name, res->hostname_dn, res->hostname_dn_len) != 0) { + if (query && dns_hostname_cmp(query->name, res->hostname_dn, res->hostname_dn_len) != 0) { dns_resp = DNS_RESP_WRONG_NAME; ns->counters.other++; goto report_res_error; diff -Nru haproxy-2.1.11/src/fcgi-app.c haproxy-2.1.12/src/fcgi-app.c --- haproxy-2.1.11/src/fcgi-app.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/fcgi-app.c 2021-03-18 13:25:32.000000000 +0000 @@ -478,7 +478,7 @@ _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1); if (sess->fe != s->be) _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1); hdr_rule_err: node = ebpt_first(&hdr_rules); diff -Nru haproxy-2.1.11/src/filters.c haproxy-2.1.12/src/filters.c --- haproxy-2.1.11/src/filters.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/filters.c 2021-03-18 13:25:32.000000000 +0000 @@ -476,6 +476,8 @@ if (FLT_OPS(filter)->stream_start && FLT_OPS(filter)->stream_start(s, filter) < 0) return -1; } + if (strm_li(s) && (strm_li(s)->analysers & AN_REQ_FLT_START_FE)) + s->req.flags |= CF_FLT_ANALYZE; return 0; } @@ -534,6 +536,10 @@ FLT_OPS(filter)->stream_set_backend(s, filter, be) < 0) return -1; } + if (be->be_req_ana & AN_REQ_FLT_START_BE) + s->req.flags |= CF_FLT_ANALYZE; + if ((strm_fe(s)->fe_rsp_ana | be->be_rsp_ana) & (AN_RES_FLT_START_FE|AN_RES_FLT_START_BE)) + s->res.flags |= CF_FLT_ANALYZE; return 0; } @@ -635,6 +641,8 @@ unsigned int out = co_data(msg->chn); int ret, data; + strm_flt(s)->flags &= ~STRM_FLT_FL_HOLD_HTTP_HDRS; + ret = data = len - out; DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s, s->txn, msg); list_for_each_entry(filter, &strm_flt(s)->filters, list) { @@ -659,11 +667,22 @@ } } - /* Only forward data if the last filter decides to forward something */ - if (ret > 0) { - ret = data; - *strm_off += ret; - } + /* If nothing was forwarded yet, we take care to hold the headers if + * following conditions are met : + * + * - *strm_off == 0 (nothing forwarded yet) + * - ret == 0 (no data forwarded at all on this turn) + * - STRM_FLT_FL_HOLD_HTTP_HDRS flag set (at least one filter want to hold the headers) + * + * Be careful, STRM_FLT_FL_HOLD_HTTP_HDRS is removed before each http_payload loop. + * Thus, it must explicitly be set when necessary. We must do that to hold the headers + * when there is no payload. + */ + if (!ret && !*strm_off && (strm_flt(s)->flags & STRM_FLT_FL_HOLD_HTTP_HDRS)) + goto end; + + ret = data; + *strm_off += ret; end: DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_FLT_ANA, s); return ret; diff -Nru haproxy-2.1.11/src/flt_spoe.c haproxy-2.1.12/src/flt_spoe.c --- haproxy-2.1.11/src/flt_spoe.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/flt_spoe.c 2021-03-18 13:25:32.000000000 +0000 @@ -1464,8 +1464,6 @@ goto stop; default: - /* HELLO handshake is finished, set the idle timeout and - * add the applet in the list of running applets. */ _HA_ATOMIC_ADD(&agent->counters.idles, 1); appctx->st0 = SPOE_APPCTX_ST_IDLE; SPOE_APPCTX(appctx)->node.key = 0; @@ -1753,6 +1751,20 @@ } if (appctx->st0 == SPOE_APPCTX_ST_PROCESSING && SPOE_APPCTX(appctx)->cur_fpa < agent->max_fpa) { + struct server *srv = objt_server(si_strm(si)->target); + + /* With several threads, close the applet if there are pending + * connections or if the server is full. Otherwise, add the + * applet in the idle list. + */ + if (global.nbthread > 1 && + (agent->b.be->nbpend || + (srv && (srv->nbpend || (srv->maxconn && srv->served >=srv_dynamic_maxconn(srv)))))) { + SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE; + appctx->st0 = SPOE_APPCTX_ST_DISCONNECT; + appctx->st1 = SPOE_APPCTX_ERR_NONE; + goto next; + } _HA_ATOMIC_ADD(&agent->counters.idles, 1); appctx->st0 = SPOE_APPCTX_ST_IDLE; eb32_insert(&agent->rt[tid].idle_applets, &SPOE_APPCTX(appctx)->node); diff -Nru haproxy-2.1.11/src/freq_ctr.c haproxy-2.1.12/src/freq_ctr.c --- haproxy-2.1.11/src/freq_ctr.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/freq_ctr.c 2021-03-18 13:25:32.000000000 +0000 @@ -52,7 +52,7 @@ break; } - age = now.tv_sec - curr_sec; + age = (global_now >> 32) - curr_sec; if (unlikely(age > 1)) return 0; @@ -95,7 +95,7 @@ break; } - age = now.tv_sec - curr_sec; + age = (global_now >> 32) - curr_sec; if (unlikely(age > 1)) curr = 0; else { @@ -142,7 +142,7 @@ break; } - age = now.tv_sec - curr_sec; + age = (global_now >> 32) - curr_sec; if (unlikely(age > 1)) curr = 0; else { @@ -164,7 +164,7 @@ /* Reads a frequency counter taking history into account for missing time in * current period. The period has to be passed in number of ticks and must * match the one used to feed the counter. The counter value is reported for - * current date (now_ms). The return value has the same precision as one input + * current global date. The return value has the same precision as one input * data sample, so low rates over the period will be inaccurate but still * appropriate for max checking. One trick we use for low values is to specially * handle the case where the rate is between 0 and 1 in order to avoid flapping @@ -201,7 +201,7 @@ break; }; - remain = curr_tick + period - now_ms; + remain = curr_tick + period - (uint32_t)global_now / 1000; if (unlikely((int)remain < 0)) { /* We're past the first period, check if we can still report a * part of last period or if we're too far away. @@ -248,7 +248,7 @@ break; }; - remain = curr_tick + period - now_ms; + remain = curr_tick + period - (uint32_t)global_now / 1000; if (likely((int)remain < 0)) { /* We're past the first period, check if we can still report a * part of last period or if we're too far away. diff -Nru haproxy-2.1.11/src/haproxy.c haproxy-2.1.12/src/haproxy.c --- haproxy-2.1.11/src/haproxy.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/haproxy.c 2021-03-18 13:25:32.000000000 +0000 @@ -1966,7 +1966,8 @@ /* in wait mode, we don't try to read the configuration files */ if (!(global.mode & MODE_MWORKER_WAIT)) { - struct buffer *trash = get_trash_chunk(); + char *env_cfgfiles = NULL; + int env_err = 0; /* handle cfgfiles that are actually directories */ cfgfiles_expand_directories(); @@ -1978,22 +1979,27 @@ list_for_each_entry(wl, &cfg_cfgfiles, list) { int ret; - if (trash->data) - chunk_appendf(trash, ";"); - - chunk_appendf(trash, "%s", wl->s); + if (env_err == 0) { + if (!memprintf(&env_cfgfiles, "%s%s%s", + (env_cfgfiles ? env_cfgfiles : ""), + (env_cfgfiles ? ";" : ""), wl->s)) + env_err = 1; + } ret = readcfgfile(wl->s); if (ret == -1) { ha_alert("Could not open configuration file %s : %s\n", wl->s, strerror(errno)); + free(env_cfgfiles); exit(1); } if (ret & (ERR_ABORT|ERR_FATAL)) ha_alert("Error(s) found in configuration file : %s\n", wl->s); err_code |= ret; - if (err_code & ERR_ABORT) + if (err_code & ERR_ABORT) { + free(env_cfgfiles); exit(1); + } } /* do not try to resolve arguments nor to spot inconsistencies when @@ -2002,10 +2008,15 @@ */ if (err_code & (ERR_ABORT|ERR_FATAL)) { ha_alert("Fatal errors found in configuration.\n"); + free(env_cfgfiles); exit(1); } - if (trash->data) - setenv("HAPROXY_CFGFILES", trash->area, 1); + if (env_err) { + ha_alert("Could not allocate memory for HAPROXY_CFGFILES env variable\n"); + exit(1); + } + setenv("HAPROXY_CFGFILES", env_cfgfiles, 1); + free(env_cfgfiles); } if (global.mode & MODE_MWORKER) { @@ -2561,11 +2572,15 @@ free(p->cookie_domain); free(p->cookie_attrs); free(p->lbprm.arg_str); + free(p->server_state_file_name); free(p->capture_name); free(p->monitor_uri); free(p->rdp_cookie_name); free(p->invalid_rep); free(p->invalid_req); +#if defined(CONFIG_HAP_TRANSPARENT) + free(p->conn_src.bind_hdr_name); +#endif if (p->conf.logformat_string != default_http_log_format && p->conf.logformat_string != default_tcp_log_format && p->conf.logformat_string != clf_http_log_format) @@ -2805,6 +2820,8 @@ free(global.desc); global.desc = NULL; free(oldpids); oldpids = NULL; task_destroy(global_listener_queue_task); global_listener_queue_task = NULL; + free(global.server_state_base); global.server_state_base = NULL; + free(global.server_state_file); global.server_state_file = NULL; task_destroy(idle_conn_task); idle_conn_task = NULL; @@ -3104,8 +3121,7 @@ if (global.tune.options & GTUNE_STRICT_LIMITS) { ha_alert("[%s.main()] Cannot raise FD limit to %d, limit is %d.\n", argv[0], global.rlimit_nofile, (int)limit.rlim_cur); - if (!(global.mode & MODE_MWORKER)) - exit(1); + exit(1); } else { /* try to set it to the max possible at least */ @@ -3129,8 +3145,7 @@ if (global.tune.options & GTUNE_STRICT_LIMITS) { ha_alert("[%s.main()] Cannot fix MEM limit to %d megs.\n", argv[0], global.rlimit_memmax); - if (!(global.mode & MODE_MWORKER)) - exit(1); + exit(1); } else ha_warning("[%s.main()] Cannot fix MEM limit to %d megs." @@ -3142,8 +3157,7 @@ if (global.tune.options & GTUNE_STRICT_LIMITS) { ha_alert("[%s.main()] Cannot fix MEM limit to %d megs.\n", argv[0], global.rlimit_memmax); - if (!(global.mode & MODE_MWORKER)) - exit(1); + exit(1); } else ha_warning("[%s.main()] Cannot fix MEM limit to %d megs." @@ -3328,8 +3342,7 @@ "Please raise 'ulimit-n' to %d or more to avoid any trouble.\n", argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock, global.maxsock); - if (!(global.mode & MODE_MWORKER)) - exit(1); + exit(1); } else ha_alert("[%s.main()] FD limit (%d) too low for maxconn=%d/maxsock=%d. " @@ -3611,8 +3624,7 @@ if (global.tune.options & GTUNE_STRICT_LIMITS) { ha_alert("[%s.main()] Failed to set the raise the maximum " "file size.\n", argv[0]); - if (!(global.mode & MODE_MWORKER)) - exit(1); + exit(1); } else ha_warning("[%s.main()] Failed to set the raise the maximum " @@ -3625,8 +3637,7 @@ if (global.tune.options & GTUNE_STRICT_LIMITS) { ha_alert("[%s.main()] Failed to set the raise the core " "dump size.\n", argv[0]); - if (!(global.mode & MODE_MWORKER)) - exit(1); + exit(1); } else ha_warning("[%s.main()] Failed to set the raise the core " diff -Nru haproxy-2.1.11/src/hathreads.c haproxy-2.1.12/src/hathreads.c --- haproxy-2.1.11/src/hathreads.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/hathreads.c 2021-03-18 13:25:32.000000000 +0000 @@ -182,6 +182,8 @@ if (cpuset_getaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1, sizeof(cpuset), &cpuset) == 0) ret = CPU_COUNT(&cpuset); +#elif defined(__APPLE__) + ret = (int)sysconf(_SC_NPROCESSORS_ONLN); #endif #endif ret = MAX(ret, 1); diff -Nru haproxy-2.1.11/src/hlua.c haproxy-2.1.12/src/hlua.c --- haproxy-2.1.11/src/hlua.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/hlua.c 2021-03-18 13:25:32.000000000 +0000 @@ -10,6 +10,8 @@ * */ +#define _GNU_SOURCE + #include #include #include diff -Nru haproxy-2.1.11/src/hlua_fcn.c haproxy-2.1.12/src/hlua_fcn.c --- haproxy-2.1.11/src/hlua_fcn.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/hlua_fcn.c 2021-03-18 13:25:32.000000000 +0000 @@ -14,6 +14,9 @@ * in an environment able to catch a longjmp, otherwise a * critical error can be raised. */ + +#define _GNU_SOURCE + #include #include #include @@ -169,12 +172,13 @@ const char *hlua_pushstrippedstring(lua_State *L, const char *str) { const char *p; - const char *e; + int l; for (p = str; HTTP_IS_LWS(*p); p++); - for (e = p + strlen(p) - 1; e > p && HTTP_IS_LWS(*e); e--); - return lua_pushlstring(L, p, e - p); + for (l = strlen(p); l && HTTP_IS_LWS(p[l-1]); l--); + + return lua_pushlstring(L, p, l); } /* The three following functions are useful for adding entries diff -Nru haproxy-2.1.11/src/http_ana.c haproxy-2.1.12/src/http_ana.c --- haproxy-2.1.11/src/http_ana.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/http_ana.c 2021-03-18 13:25:32.000000000 +0000 @@ -95,10 +95,11 @@ /* Parsing errors are caught here */ if (htx->flags & (HTX_FL_PARSING_ERROR|HTX_FL_PROCESSING_ERROR)) { stream_inc_http_req_ctr(s); - stream_inc_http_err_ctr(s); proxy_inc_fe_req_ctr(sess->fe); - if (htx->flags & HTX_FL_PARSING_ERROR) + if (htx->flags & HTX_FL_PARSING_ERROR) { + stream_inc_http_err_ctr(s); goto return_bad_req; + } else goto return_int_err; } @@ -145,11 +146,10 @@ if (sess->fe->options & PR_O_IGNORE_PRB) goto failed_keep_alive; - stream_inc_http_err_ctr(s); stream_inc_http_req_ctr(s); proxy_inc_fe_req_ctr(sess->fe); _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); txn->status = 400; @@ -172,11 +172,10 @@ if (sess->fe->options & PR_O_IGNORE_PRB) goto failed_keep_alive; - stream_inc_http_err_ctr(s); stream_inc_http_req_ctr(s); proxy_inc_fe_req_ctr(sess->fe); _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); txn->status = 408; @@ -203,7 +202,7 @@ stream_inc_http_req_ctr(s); proxy_inc_fe_req_ctr(sess->fe); _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); txn->status = 400; @@ -219,7 +218,7 @@ req->flags |= CF_READ_DONTWAIT; /* try to get back here ASAP */ s->res.flags &= ~CF_EXPECT_MORE; /* speed up sending a previous response */ - if (sess->listener->options & LI_O_NOQUICKACK && htx_is_not_empty(htx) && + if (sess->listener && (sess->listener->options & LI_O_NOQUICKACK) && htx_is_not_empty(htx) && objt_conn(sess->origin) && conn_ctrl_ready(__objt_conn(sess->origin))) { /* We need more data, we have to re-enable quick-ack in case we * previously disabled it, otherwise we might cause the client @@ -436,14 +435,14 @@ if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_INTERNAL; _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); goto return_prx_cond; return_bad_req: txn->status = 400; _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); /* fall through */ @@ -651,7 +650,7 @@ _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1); if (sess->fe != s->be) _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1); goto done_without_exp; @@ -670,7 +669,7 @@ _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1); if (sess->fe != s->be) _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1); goto return_prx_cond; @@ -679,7 +678,7 @@ http_reply_and_close(s, txn->status, http_error_message(s)); _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); return_prx_cond: @@ -864,7 +863,7 @@ if ((sess->fe->options | s->be->options) & PR_O_ORGTO) { /* FIXME: don't know if IPv6 can handle that case too. */ - if (cli_conn && conn_get_src(cli_conn) && cli_conn->src->ss_family == AF_INET && conn_get_dst(cli_conn)) { + if (cli_conn && conn_get_dst(cli_conn) && cli_conn->dst->ss_family == AF_INET) { /* Add an X-Original-To header unless the destination IP is * in the 'except' network range. */ @@ -914,7 +913,7 @@ * in case we previously disabled it, otherwise we might cause * the client to delay further data. */ - if ((sess->listener->options & LI_O_NOQUICKACK) && + if (sess->listener && (sess->listener->options & LI_O_NOQUICKACK) && (htx_get_tail_type(htx) != HTX_BLK_EOM)) conn_set_quickack(cli_conn, 1); @@ -936,7 +935,7 @@ http_reply_and_close(s, txn->status, http_error_message(s)); _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); if (!(s->flags & SF_ERR_MASK)) @@ -1103,7 +1102,7 @@ req->analysers &= AN_REQ_FLT_END; _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn); @@ -1346,7 +1345,7 @@ return_bad_req: _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1); if (!(s->flags & SF_ERR_MASK)) s->flags |= SF_ERR_CLICL; @@ -1962,7 +1961,7 @@ _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1); _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_resp, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->denied_resp, 1); goto return_srv_prx_502; } @@ -2099,7 +2098,7 @@ _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1); _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_resp, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->denied_resp, 1); ha_alert("Blocking cacheable cookie in response from instance %s, server %s.\n", @@ -3003,7 +3002,7 @@ _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1); if (sess->fe != s->be) _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1); } free_trash_chunk(replace); @@ -3342,7 +3341,7 @@ _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_rewrites, 1); if (sess->fe != s->be) _HA_ATOMIC_ADD(&s->be->be_counters.failed_rewrites, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->failed_rewrites, 1); if (objt_server(s->target)) _HA_ATOMIC_ADD(&objt_server(s->target)->counters.failed_rewrites, 1); diff -Nru haproxy-2.1.11/src/mux_h1.c haproxy-2.1.12/src/mux_h1.c --- haproxy-2.1.11/src/mux_h1.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/mux_h1.c 2021-03-18 13:25:32.000000000 +0000 @@ -1389,12 +1389,17 @@ return 0; } + h1s->flags &= ~H1S_F_APPEND_EOM; h1m->state = H1_MSG_DONE; - /* Don't set EOI on the conn-stream for protocol upgrade requests, wait - * the response to do so or not depending on the status code. - */ - if (!(h1m->flags & H1_MF_CONN_UPG)) + /* Set EOI on conn-stream in DONE state iff: + * - it is a response + * - it is a request but no a protocol upgrade nor a CONNECT + * + * If not set, Wait the response to do so or not depending on the status + * code. + */ + if ((h1m->flags & H1_MF_RESP) || ((h1s->meth != HTTP_METH_CONNECT) && !(h1m->flags & H1_MF_CONN_UPG))) h1s->cs->flags |= CS_FL_EOI; TRACE_STATE("end of message", H1_EV_RX_DATA|H1_EV_RX_EOI, h1s->h1c->conn, h1s); TRACE_LEAVE(H1_EV_RX_DATA, h1s->h1c->conn, h1s); @@ -1482,6 +1487,10 @@ else if (h1m->state == H1_MSG_DONE) { if (!(h1m->flags & H1_MF_RESP) && h1s->status == 101) h1_set_req_tunnel_mode(h1s); + else if ((h1m->flags & H1_MF_RESP) && h1s->req.state == H1_MSG_TUNNEL) { + TRACE_STATE("switch back H1 request from tunnel mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1c->conn, h1s); + h1s->req.state = H1_MSG_DONE; + } else if (h1s->req.state < H1_MSG_DONE || h1s->res.state < H1_MSG_DONE) { h1c->flags |= H1C_F_IN_BUSY; TRACE_STATE("switch h1c in busy mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1c->conn, h1s); @@ -1936,7 +1945,12 @@ h1_set_req_tunnel_mode(h1s); TRACE_STATE("switch H1 request in tunnel mode", H1_EV_TX_DATA|H1_EV_TX_HDRS, h1c->conn, h1s); } - else if (h1s->h1c->flags & H1C_F_IN_BUSY) { + else if ((h1m->flags & H1_MF_RESP) && h1s->req.state == H1_MSG_TUNNEL) { + TRACE_STATE("switch back H1 request from tunnel mode", H1_EV_RX_DATA|H1_EV_H1C_BLK, h1c->conn, h1s); + h1s->req.state = H1_MSG_DONE; + } + + if (h1s->h1c->flags & H1C_F_IN_BUSY) { h1s->h1c->flags &= ~H1C_F_IN_BUSY; tasklet_wakeup(h1s->h1c->wait_event.tasklet); TRACE_STATE("h1c no more busy", H1_EV_TX_DATA|H1_EV_H1C_BLK|H1_EV_H1C_WAKE, h1c->conn, h1s); @@ -2698,6 +2712,12 @@ return 0; } + if (h1c->flags & H1C_F_CS_ERROR) { + cs->flags |= CS_FL_ERROR; + TRACE_DEVEL("H1 connection is in error, leaving in error", H1_EV_STRM_SEND|H1_EV_H1C_ERR|H1_EV_H1S_ERR|H1_EV_STRM_ERR, h1c->conn, h1s); + return 0; + } + while (count) { size_t ret = 0; @@ -2712,6 +2732,12 @@ if ((h1c->wait_event.events & SUB_RETRY_SEND) || !h1_send(h1c)) break; } + + if (h1c->flags & H1C_F_CS_ERROR) { + TRACE_DEVEL("reporting error to the app-layer stream", H1_EV_STRM_SEND|H1_EV_H1S_ERR|H1_EV_STRM_ERR, h1c->conn, h1s); + cs->flags |= CS_FL_ERROR; + } + h1_refresh_timeout(h1c); TRACE_LEAVE(H1_EV_STRM_SEND, h1c->conn, h1s,, (size_t[]){total}); return total; diff -Nru haproxy-2.1.11/src/mux_h2.c haproxy-2.1.12/src/mux_h2.c --- haproxy-2.1.11/src/mux_h2.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/mux_h2.c 2021-03-18 13:25:32.000000000 +0000 @@ -63,7 +63,9 @@ #define H2_CF_GOAWAY_FAILED 0x00002000 // a GOAWAY frame failed to be sent #define H2_CF_WAIT_FOR_HS 0x00004000 // We did check that at least a stream was waiting for handshake #define H2_CF_IS_BACK 0x00008000 // this is an outgoing connection -#define H2_CF_WINDOW_OPENED 0x00010000 // demux increased window already advertised +#define H2_CF_WINDOW_OPENED 0x00010000 // demux increased window already advertised +#define H2_CF_RCVD_SHUT 0x00020000 // a recv() attempt already failed on a shutdown +#define H2_CF_END_REACHED 0x00040000 // pending data too short with RCVD_SHUT present /* H2 connection state, in h2c->st0 */ enum h2_cs { @@ -552,15 +554,15 @@ } -/* Detect a pending read0 for a H2 connection. It happens if a read0 is pending - * on the connection AND if there is no more data in the demux buffer. The - * function returns 1 to report a read0 or 0 otherwise. +/* Detect a pending read0 for a H2 connection. It happens if a read0 was + * already reported on a previous xprt->rcvbuf() AND a frame parser failed + * to parse pending data, confirming no more progress is possible because + * we're facing a truncated frame. The function returns 1 to report a read0 + * or 0 otherwise. */ -static int h2c_read0_pending(struct h2c *h2c) +static inline int h2c_read0_pending(struct h2c *h2c) { - if (conn_xprt_read0_pending(h2c->conn) && !b_data(&h2c->dbuf)) - return 1; - return 0; + return !!(h2c->flags & H2_CF_END_REACHED); } /* returns true if the connection is allowed to expire, false otherwise. A @@ -2971,7 +2973,7 @@ if (!b_data(&h2c->dbuf)) { TRACE_DEVEL("no more Rx data", H2_EV_RX_FRAME, h2c->conn); - break; + goto dbuf_empty; } if (h2c->st0 >= H2_CS_ERROR) { @@ -3202,9 +3204,12 @@ ret = h2c_send_rst_stream(h2c, h2s); } + dbuf_empty: /* error or missing data condition met above ? */ if (ret <= 0) { TRACE_DEVEL("insufficient data to proceed", H2_EV_RX_FRAME, h2c->conn, h2s); + if (h2c->flags & H2_CF_RCVD_SHUT) + h2c->flags |= H2_CF_END_REACHED; break; } @@ -3228,8 +3233,7 @@ h2c_send_conn_wu(h2c); } - fail: - /* we can go here on missing data, blocked response or error */ + done: if (h2s && h2s->cs && (b_data(&h2s->rxbuf) || h2c_read0_pending(h2c) || @@ -3250,6 +3254,15 @@ h2c_restart_reading(h2c, 0); out: TRACE_LEAVE(H2_EV_H2C_WAKE, h2c->conn); + return; + + fail: + /* we can go here on missing data, blocked response or error, but we + * need to check if we've met a short read condition. + */ + if (h2c->flags & H2_CF_RCVD_SHUT) + h2c->flags |= H2_CF_END_REACHED; + goto done; } /* resume each h2s eligible for sending in list head */ @@ -3376,6 +3389,11 @@ return 0; } + if (h2c->flags & H2_CF_RCVD_SHUT) { + TRACE_DEVEL("leaving on rcvd_shut", H2_EV_H2C_RECV, h2c->conn); + return 0; + } + b_realign_if_empty(buf); if (!b_data(buf)) { /* try to pre-align the buffer like the @@ -3394,9 +3412,14 @@ ret = max ? conn->xprt->rcv_buf(conn, conn->xprt_ctx, buf, max, 0) : 0; - if (max && !ret && h2_recv_allowed(h2c)) { - TRACE_DATA("failed to receive data, subscribing", H2_EV_H2C_RECV, h2c->conn); - conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV, &h2c->wait_event); + if (max && !ret) { + if (conn_xprt_read0_pending(h2c->conn)) { + TRACE_DATA("received read0", H2_EV_H2C_RECV, h2c->conn); + h2c->flags |= H2_CF_RCVD_SHUT; + } else if (h2_recv_allowed(h2c)) { + TRACE_DATA("failed to receive data, subscribing", H2_EV_H2C_RECV, h2c->conn); + conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV, &h2c->wait_event); + } } else if (ret) TRACE_DATA("received data", H2_EV_H2C_RECV, h2c->conn,,, (void*)(long)ret); @@ -3556,7 +3579,8 @@ TRACE_ENTER(H2_EV_H2C_WAKE, conn); - if (b_data(&h2c->dbuf) && !(h2c->flags & H2_CF_DEM_BLOCK_ANY)) { + if (!(h2c->flags & H2_CF_DEM_BLOCK_ANY) && + (b_data(&h2c->dbuf) || (h2c->flags & H2_CF_RCVD_SHUT))) { h2_process_demux(h2c); if (h2c->st0 >= H2_CS_ERROR || conn->flags & CO_FL_ERROR) @@ -4971,7 +4995,7 @@ if (len + 2 < uri.len && uri.ptr[len + 1] == '/' && uri.ptr[len + 2] == '/') { /* make the uri start at the authority now */ scheme.ptr = uri.ptr; - scheme.len = len, + scheme.len = len; uri.ptr += len + 3; uri.len -= len + 3; @@ -6043,7 +6067,7 @@ (unsigned int)b_head_ofs(tmbuf), (unsigned int)b_size(tmbuf)); if (h2s) { - chunk_appendf(msg, " last_h2s=%p .id=%d .st=%s.flg=0x%04x .rxbuf=%u@%p+%u/%u .cs=%p", + chunk_appendf(msg, " last_h2s=%p .id=%d .st=%s .flg=0x%04x .rxbuf=%u@%p+%u/%u .cs=%p", h2s, h2s->id, h2s_st_to_str(h2s->st), h2s->flags, (unsigned int)b_data(&h2s->rxbuf), b_orig(&h2s->rxbuf), (unsigned int)b_head_ofs(&h2s->rxbuf), (unsigned int)b_size(&h2s->rxbuf), diff -Nru haproxy-2.1.11/src/mworker.c haproxy-2.1.12/src/mworker.c --- haproxy-2.1.11/src/mworker.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/mworker.c 2021-03-18 13:25:32.000000000 +0000 @@ -10,6 +10,8 @@ * */ +#define _GNU_SOURCE + #include #include #include diff -Nru haproxy-2.1.11/src/peers.c haproxy-2.1.12/src/peers.c --- haproxy-2.1.11/src/peers.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/peers.c 2021-03-18 13:25:32.000000000 +0000 @@ -2542,6 +2542,7 @@ struct session *sess; struct stream *s; + peer->new_conn++; peer->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT)); peer->heartbeat = TICK_ETERNITY; peer->statuscode = PEER_SESS_SC_CONNECTCODE; @@ -2666,7 +2667,6 @@ /* reschedule task for reconnect */ task->expire = tick_first(task->expire, ps->reconnect); - ps->new_conn++; } /* else do nothing */ } /* !ps->appctx */ diff -Nru haproxy-2.1.11/src/proto_tcp.c haproxy-2.1.12/src/proto_tcp.c --- haproxy-2.1.11/src/proto_tcp.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/proto_tcp.c 2021-03-18 13:25:32.000000000 +0000 @@ -1234,6 +1234,7 @@ if (smp->data.type == SMP_T_IPV4) { ((struct sockaddr_in *)cli_conn->dst)->sin_family = AF_INET; ((struct sockaddr_in *)cli_conn->dst)->sin_addr.s_addr = smp->data.u.ipv4.s_addr; + ((struct sockaddr_in *)cli_conn->dst)->sin_port = port; } else if (smp->data.type == SMP_T_IPV6) { ((struct sockaddr_in6 *)cli_conn->dst)->sin6_family = AF_INET6; memcpy(&((struct sockaddr_in6 *)cli_conn->dst)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr)); @@ -1366,7 +1367,7 @@ } _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1); - if (sess->listener->counters) + if (sess->listener && sess->listener->counters) _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1); return ACT_RET_STOP; diff -Nru haproxy-2.1.11/src/proxy.c haproxy-2.1.12/src/proxy.c --- haproxy-2.1.11/src/proxy.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/proxy.c 2021-03-18 13:25:32.000000000 +0000 @@ -1075,11 +1075,15 @@ { struct proxy *p; struct stream *s; + int thr; if (killed) { ha_warning("Some tasks resisted to hard-stop, exiting now.\n"); send_log(NULL, LOG_WARNING, "Some tasks resisted to hard-stop, exiting now.\n"); killed = 2; + for (thr = 0; thr < global.nbthread; thr++) + if (((all_threads_mask & ~tid_bit) >> thr) & 1) + wake_thread(thr); t->expire = TICK_ETERNITY; return t; } @@ -1096,9 +1100,12 @@ } p = p->next; } + + thread_isolate(); list_for_each_entry(s, &streams, list) { stream_shutdown(s, SF_ERR_KILLED); } + thread_release(); killed = 1; t->expire = tick_add(now_ms, MS_TO_TICKS(1000)); diff -Nru haproxy-2.1.11/src/sample.c haproxy-2.1.12/src/sample.c --- haproxy-2.1.11/src/sample.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/sample.c 2021-03-18 13:25:32.000000000 +0000 @@ -831,7 +831,7 @@ const char *begw; /* beginning of word */ const char *endw; /* end of word */ const char *endt; /* end of term */ - struct sample_expr *expr; + struct sample_expr *expr = NULL; struct sample_fetch *fetch; struct sample_conv *conv; unsigned long prev_type; @@ -1023,7 +1023,7 @@ return expr; out_error: - /* TODO: prune_sample_expr(expr); */ + release_sample_expr(expr); expr = NULL; goto out; } @@ -2751,6 +2751,9 @@ int max; trash = alloc_trash_chunk(); + if (!trash) + return 0; + trash->data = smp->data.u.str.data; if (trash->data > trash->size - 1) trash->data = trash->size - 1; diff -Nru haproxy-2.1.11/src/server.c haproxy-2.1.12/src/server.c --- haproxy-2.1.11/src/server.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/server.c 2021-03-18 13:25:32.000000000 +0000 @@ -2929,7 +2929,7 @@ /* inherited statuses will be recomputed later. * Also disable SRV_ADMF_HMAINT flag (set from stats socket fqdn). */ - srv_admin_state &= ~SRV_ADMF_IDRAIN & ~SRV_ADMF_IMAINT & ~SRV_ADMF_HMAINT; + srv_admin_state &= ~SRV_ADMF_IDRAIN & ~SRV_ADMF_IMAINT & ~SRV_ADMF_HMAINT & ~SRV_ADMF_RMAINT; if ((p == params[2]) || errno == EINVAL || errno == ERANGE || (srv_admin_state != 0 && @@ -3322,50 +3322,55 @@ buf[buflen - 1] = '\0'; /* we're now ready to move the line into *srv_params[] */ - params[0] = cur; - arg = 1; + memset(params, 0, SRV_STATE_FILE_MAX_FIELDS * sizeof(*params)); + memset(srv_params, 0, SRV_STATE_FILE_MAX_FIELDS * sizeof(*srv_params)); + + arg = 0; srv_arg = 0; while (*cur && arg < SRV_STATE_FILE_MAX_FIELDS) { - if (isspace(*cur)) { - *cur = '\0'; + /* Search begining of the current field */ + while (isspace((unsigned char)*cur)) { ++cur; - while (isspace(*cur)) - ++cur; - switch (version) { - case 1: - /* - * srv_addr: params[4] => srv_params[0] - * srv_op_state: params[5] => srv_params[1] - * srv_admin_state: params[6] => srv_params[2] - * srv_uweight: params[7] => srv_params[3] - * srv_iweight: params[8] => srv_params[4] - * srv_last_time_change: params[9] => srv_params[5] - * srv_check_status: params[10] => srv_params[6] - * srv_check_result: params[11] => srv_params[7] - * srv_check_health: params[12] => srv_params[8] - * srv_check_state: params[13] => srv_params[9] - * srv_agent_state: params[14] => srv_params[10] - * bk_f_forced_id: params[15] => srv_params[11] - * srv_f_forced_id: params[16] => srv_params[12] - * srv_fqdn: params[17] => srv_params[13] - * srv_port: params[18] => srv_params[14] - * srvrecord: params[19] => srv_params[15] - */ - if (arg >= 4) { - srv_params[srv_arg] = cur; - ++srv_arg; - } - break; - } - - params[arg] = cur; - ++arg; + if (!*cur) + goto end; } - else { + + /* v1 + * srv_addr: params[4] => srv_params[0] + * srv_op_state: params[5] => srv_params[1] + * srv_admin_state: params[6] => srv_params[2] + * srv_uweight: params[7] => srv_params[3] + * srv_iweight: params[8] => srv_params[4] + * srv_last_time_change: params[9] => srv_params[5] + * srv_check_status: params[10] => srv_params[6] + * srv_check_result: params[11] => srv_params[7] + * srv_check_health: params[12] => srv_params[8] + * srv_check_state: params[13] => srv_params[9] + * srv_agent_state: params[14] => srv_params[10] + * bk_f_forced_id: params[15] => srv_params[11] + * srv_f_forced_id: params[16] => srv_params[12] + * srv_fqdn: params[17] => srv_params[13] + * srv_port: params[18] => srv_params[14] + * srvrecord: params[19] => srv_params[15] + */ + if (version == 1 && arg >= 4) { + srv_params[srv_arg] = cur; + ++srv_arg; + } + params[arg] = cur; + ++arg; + + /* Search end of the current field: first space or \0 */ + /* Search begining of the current field */ + while (!isspace((unsigned char)*cur)) { ++cur; + if (!*cur) + goto end; } + *cur++ = '\0'; } + end: /* if line is incomplete line, then ignore it. * otherwise, update useful flags */ switch (version) { @@ -3588,10 +3593,10 @@ localfilepathlen += len; localfilepath[localfilepathlen++] = 0; } - filepath = localfilepath; localfileerror: if (localfilepathlen == 0) - localfilepath[0] = '\0'; + continue; + filepath = localfilepath; break; case PR_SRV_STATE_FILE_NONE: @@ -4162,6 +4167,80 @@ } /* + * SRV record error management callback + * returns: + * 0 on error + * 1 when no error or safe ignore + * + * Grabs the server's lock. + */ +int srvrq_resolution_error_cb(struct dns_requester *requester, int error_code) +{ + struct server *s; + struct dns_srvrq *srvrq; + struct dns_resolution *res; + struct dns_resolvers *resolvers; + int exp; + + /* SRV records */ + srvrq = objt_dns_srvrq(requester->owner); + if (!srvrq) + return 1; + + resolvers = srvrq->resolvers; + res = requester->resolution; + + switch (res->status) { + + case RSLV_STATUS_NX: + /* stop server if resolution is NX for a long enough period */ + exp = tick_add(res->last_valid, resolvers->hold.nx); + if (!tick_is_expired(exp, now_ms)) + return 1; + break; + + case RSLV_STATUS_TIMEOUT: + /* stop server if resolution is TIMEOUT for a long enough period */ + exp = tick_add(res->last_valid, resolvers->hold.timeout); + if (!tick_is_expired(exp, now_ms)) + return 1; + break; + + case RSLV_STATUS_REFUSED: + /* stop server if resolution is REFUSED for a long enough period */ + exp = tick_add(res->last_valid, resolvers->hold.refused); + if (!tick_is_expired(exp, now_ms)) + return 1; + break; + + default: + /* stop server if resolution failed for a long enough period */ + exp = tick_add(res->last_valid, resolvers->hold.other); + if (!tick_is_expired(exp, now_ms)) + return 1; + } + + /* Remove any associated server */ + for (s = srvrq->proxy->srv; s != NULL; s = s->next) { + HA_SPIN_LOCK(SERVER_LOCK, &s->lock); + if (s->srvrq == srvrq) { + snr_update_srv_status(s, 1); + free(s->hostname); + free(s->hostname_dn); + s->hostname = NULL; + s->hostname_dn = NULL; + s->hostname_dn_len = 0; + memset(&s->addr, 0, sizeof(s->addr)); + s->svc_port = 0; + dns_unlink_resolution(s->dns_requester); + } + HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock); + } + + return 1; +} + +/* * Server Name Resolution error management callback * returns: * 0 on error @@ -4177,7 +4256,8 @@ if (!s) return 1; HA_SPIN_LOCK(SERVER_LOCK, &s->lock); - snr_update_srv_status(s, 0); + if (!snr_update_srv_status(s, 1)) + memset(&s->addr, 0, sizeof(s->addr)); HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock); return 1; } @@ -4214,7 +4294,7 @@ HA_SPIN_LOCK(SERVER_LOCK, &tmpsrv->lock); if ((tmpsrv->hostname_dn == NULL) || (srv->hostname_dn_len != tmpsrv->hostname_dn_len) || - (strcmp(srv->hostname_dn, tmpsrv->hostname_dn) != 0) || + (strcasecmp(srv->hostname_dn, tmpsrv->hostname_dn) != 0) || (srv->puid == tmpsrv->puid)) { HA_SPIN_UNLOCK(SERVER_LOCK, &tmpsrv->lock); continue; @@ -4293,7 +4373,8 @@ resolution = srv->dns_requester->resolution; if (resolution && resolution->hostname_dn && - !strcmp(resolution->hostname_dn, hostname_dn)) + resolution->hostname_dn_len == hostname_dn_len && + strcasecmp(resolution->hostname_dn, hostname_dn) == 0) goto end; dns_unlink_resolution(srv->dns_requester); diff -Nru haproxy-2.1.11/src/session.c haproxy-2.1.12/src/session.c --- haproxy-2.1.11/src/session.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/session.c 2021-03-18 13:25:32.000000000 +0000 @@ -123,7 +123,7 @@ ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_SESS_CNT); if (ptr) - stktable_data_cast(ptr, sess_cnt)++; + HA_ATOMIC_ADD(&stktable_data_cast(ptr, sess_cnt), 1); ptr = stktable_data_ptr(stkctr->table, stkctr_entry(stkctr), STKTABLE_DT_SESS_RATE); if (ptr) diff -Nru haproxy-2.1.11/src/ssl_sock.c haproxy-2.1.12/src/ssl_sock.c --- haproxy-2.1.11/src/ssl_sock.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/ssl_sock.c 2021-03-18 13:25:32.000000000 +0000 @@ -461,8 +461,8 @@ ret = ERR_get_error(); if (ret == 0) return; - fprintf(stderr, "fd[%04x] OpenSSL error[0x%lx] %s: %s\n", - (unsigned short)conn->handle.fd, ret, + fprintf(stderr, "fd[%#x] OpenSSL error[0x%lx] %s: %s\n", + conn->handle.fd, ret, ERR_func_error_string(ret), ERR_reason_error_string(ret)); } } @@ -1442,9 +1442,7 @@ struct buffer *sctl; if (buf) { - tmp.area = buf; - tmp.data = strlen(buf); - tmp.size = tmp.data + 1; + chunk_initstr(&tmp, buf); src = &tmp; } else { fd = open(sctl_path, O_RDONLY); @@ -3039,6 +3037,9 @@ static struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src, struct cert_key_and_chain *dst) { + if (!src || !dst) + return NULL; + if (src->cert) { dst->cert = src->cert; X509_up_ref(src->cert); @@ -3586,6 +3587,9 @@ struct ckch_store *dst; int pathlen; + if (!src) + return NULL; + pathlen = strlen(src->path); dst = calloc(1, sizeof(*dst) + pathlen + 1); if (!dst) @@ -6000,6 +6004,9 @@ { struct ssl_sock_ctx *ctx = conn->xprt_ctx; int ret; + socklen_t lskerr; + int skerr; + if (!conn_ctrl_ready(conn)) return 0; @@ -6007,6 +6014,21 @@ if (!conn->xprt_ctx) goto out_error; + /* don't start calculating a handshake on a dead connection */ + if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH)) + goto out_error; + + /* FIXME/WT: for now we don't have a clear way to inspect the connection + * status from the lower layers, so let's check the FD directly. Ideally + * the xprt layers should provide some status indicating their knowledge + * of shutdowns or error. + */ + skerr = 0; + lskerr = sizeof(skerr); + if ((getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) < 0) || + skerr != 0) + goto out_error; + #if HA_OPENSSL_VERSION_NUMBER >= 0x10101000L /* * Check if we have early data. If we do, we have to read them @@ -10636,7 +10658,6 @@ /* Only free the ckchs there, because the SNI and instances were not generated yet */ ckchs_free(ckchs_transaction.new_ckchs); ckchs_transaction.new_ckchs = NULL; - ckchs_free(ckchs_transaction.old_ckchs); ckchs_transaction.old_ckchs = NULL; free(ckchs_transaction.path); ckchs_transaction.path = NULL; @@ -10693,12 +10714,23 @@ } #if (HA_OPENSSL_VERSION_NUMBER >= 0x1000100fL) +/* This function returns a sample struct filled with an content. + * If the contains a string, it is returned in the sample flagged as + * SMP_F_CONST. If the contains a variable descriptor, the sample is + * filled with the content of the variable by using vars_get_by_desc(). + * + * Keep in mind that the sample content may be written to a pre-allocated + * trash chunk as returned by get_trash_chunk(). + * + * This function returns 0 if an error occurs, otherwise it returns 1. + */ static inline int sample_conv_var2smp_str(const struct arg *arg, struct sample *smp) { switch (arg->type) { case ARGT_STR: smp->data.type = SMP_T_STR; smp->data.u.str = arg->data.str; + smp->flags = SMP_F_CONST; return 1; case ARGT_VAR: if (!vars_get_by_desc(&arg->data.var, smp)) @@ -10713,6 +10745,64 @@ } } +/* This function checks an and fills it with a variable type if the + * string contains a valid variable name. If failed, the function + * tries to perform a base64 decode operation on the same string, and + * fills the with the decoded content. + * + * Validation is skipped if the string is empty. + * + * This function returns 0 if the variable lookup fails and the specified + * string is not a valid base64 encoded string, as well if + * unexpected argument type is specified or memory allocation error + * occurs. Otherwise it returns 1. + */ +static inline int sample_check_arg_base64(struct arg *arg, char **err) +{ + char *dec = NULL; + int dec_size; + + if (arg->type != ARGT_STR) { + memprintf(err, "unexpected argument type"); + return 0; + } + + if (arg->data.str.data == 0) /* empty */ + return 1; + + if (vars_check_arg(arg, NULL)) + return 1; + + if (arg->data.str.data % 4) { + memprintf(err, "argument needs to be base64 encoded, and " + "can either be a string or a variable"); + return 0; + } + + dec_size = (arg->data.str.data / 4 * 3) + - (arg->data.str.area[arg->data.str.data-1] == '=' ? 1 : 0) + - (arg->data.str.area[arg->data.str.data-2] == '=' ? 1 : 0); + + if ((dec = malloc(dec_size)) == NULL) { + memprintf(err, "memory allocation error"); + return 0; + } + + dec_size = base64dec(arg->data.str.area, arg->data.str.data, dec, dec_size); + if (dec_size < 0) { + memprintf(err, "argument needs to be base64 encoded, and " + "can either be a string or a variable"); + free(dec); + return 0; + } + + /* base64 decoded */ + chunk_destroy(&arg->data.str); + arg->data.str.area = dec; + arg->data.str.data = dec_size; + return 1; +} + static int check_aes_gcm(struct arg *args, struct sample_conv *conv, const char *file, int line, char **err) { @@ -10725,10 +10815,21 @@ memprintf(err, "key size must be 128, 192 or 256 (bits)."); return 0; } - /* Try to decode a variable. */ - vars_check_arg(&args[1], NULL); - vars_check_arg(&args[2], NULL); - vars_check_arg(&args[3], NULL); + + /* Try to decode variables. */ + if (!sample_check_arg_base64(&args[1], err)) { + memprintf(err, "failed to parse nonce : %s", *err); + return 0; + } + if (!sample_check_arg_base64(&args[2], err)) { + memprintf(err, "failed to parse key : %s", *err); + return 0; + } + if (!sample_check_arg_base64(&args[3], err)) { + memprintf(err, "failed to parse aead_tag : %s", *err); + return 0; + } + return 1; } @@ -10736,36 +10837,40 @@ static int sample_conv_aes_gcm_dec(const struct arg *arg_p, struct sample *smp, void *private) { struct sample nonce, key, aead_tag; - struct buffer *smp_trash, *smp_trash_alloc; + struct buffer *smp_trash = NULL, *smp_trash_alloc = NULL; EVP_CIPHER_CTX *ctx; int dec_size, ret; - smp_set_owner(&nonce, smp->px, smp->sess, smp->strm, smp->opt); - if (!sample_conv_var2smp_str(&arg_p[1], &nonce)) - return 0; - - smp_set_owner(&key, smp->px, smp->sess, smp->strm, smp->opt); - if (!sample_conv_var2smp_str(&arg_p[2], &key)) - return 0; - - smp_set_owner(&aead_tag, smp->px, smp->sess, smp->strm, smp->opt); - if (!sample_conv_var2smp_str(&arg_p[3], &aead_tag)) - return 0; - - smp_trash = get_trash_chunk(); smp_trash_alloc = alloc_trash_chunk(); if (!smp_trash_alloc) return 0; + /* smp copy */ + smp_trash_alloc->data = smp->data.u.str.data; + if (unlikely(smp_trash_alloc->data > smp_trash_alloc->size)) + smp_trash_alloc->data = smp_trash_alloc->size; + memcpy(smp_trash_alloc->area, smp->data.u.str.area, smp_trash_alloc->data); + ctx = EVP_CIPHER_CTX_new(); if (!ctx) goto err; - dec_size = base64dec(nonce.data.u.str.area, nonce.data.u.str.data, smp_trash->area, smp_trash->size); - if (dec_size < 0) + smp_trash = alloc_trash_chunk(); + if (!smp_trash) + goto err; + + smp_set_owner(&nonce, smp->px, smp->sess, smp->strm, smp->opt); + if (!sample_conv_var2smp_str(&arg_p[1], &nonce)) goto err; - smp_trash->data = dec_size; + + if (arg_p[1].type == ARGT_VAR) { + dec_size = base64dec(nonce.data.u.str.area, nonce.data.u.str.data, smp_trash->area, smp_trash->size); + if (dec_size < 0) + goto err; + smp_trash->data = dec_size; + nonce.data.u.str = *smp_trash; + } /* Set cipher type and mode */ switch(arg_p[0].data.sint) { @@ -10780,32 +10885,47 @@ break; } - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, smp_trash->data, NULL); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce.data.u.str.data, NULL); /* Initialise IV */ - if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, (unsigned char *) smp_trash->area)) + if(!EVP_DecryptInit_ex(ctx, NULL, NULL, NULL, (unsigned char *) nonce.data.u.str.area)) goto err; - dec_size = base64dec(key.data.u.str.area, key.data.u.str.data, smp_trash->area, smp_trash->size); - if (dec_size < 0) + smp_set_owner(&key, smp->px, smp->sess, smp->strm, smp->opt); + if (!sample_conv_var2smp_str(&arg_p[2], &key)) goto err; - smp_trash->data = dec_size; + + if (arg_p[2].type == ARGT_VAR) { + dec_size = base64dec(key.data.u.str.area, key.data.u.str.data, smp_trash->area, smp_trash->size); + if (dec_size < 0) + goto err; + smp_trash->data = dec_size; + key.data.u.str = *smp_trash; + } /* Initialise key */ - if (!EVP_DecryptInit_ex(ctx, NULL, NULL, (unsigned char *) smp_trash->area, NULL)) + if (!EVP_DecryptInit_ex(ctx, NULL, NULL, (unsigned char *) key.data.u.str.area, NULL)) goto err; if (!EVP_DecryptUpdate(ctx, (unsigned char *) smp_trash->area, (int *) &smp_trash->data, - (unsigned char *) smp->data.u.str.area, (int) smp->data.u.str.data)) + (unsigned char *) smp_trash_alloc->area, (int) smp_trash_alloc->data)) goto err; - dec_size = base64dec(aead_tag.data.u.str.area, aead_tag.data.u.str.data, smp_trash_alloc->area, smp_trash_alloc->size); - if (dec_size < 0) + smp_set_owner(&aead_tag, smp->px, smp->sess, smp->strm, smp->opt); + if (!sample_conv_var2smp_str(&arg_p[3], &aead_tag)) goto err; - smp_trash_alloc->data = dec_size; + + if (arg_p[3].type == ARGT_VAR) { + dec_size = base64dec(aead_tag.data.u.str.area, aead_tag.data.u.str.data, smp_trash_alloc->area, smp_trash_alloc->size); + if (dec_size < 0) + goto err; + smp_trash_alloc->data = dec_size; + aead_tag.data.u.str = *smp_trash_alloc; + } + dec_size = smp_trash->data; - EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, smp_trash_alloc->data, (void *) smp_trash_alloc->area); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, aead_tag.data.u.str.data, (void *) aead_tag.data.u.str.area); ret = EVP_DecryptFinal_ex(ctx, (unsigned char *) smp_trash->area + smp_trash->data, (int *) &smp_trash->data); if (ret <= 0) @@ -10814,12 +10934,14 @@ smp->data.u.str.data = dec_size + smp_trash->data; smp->data.u.str.area = smp_trash->area; smp->data.type = SMP_T_BIN; - smp->flags &= ~SMP_F_CONST; + smp_dup(smp); free_trash_chunk(smp_trash_alloc); + free_trash_chunk(smp_trash); return 1; err: free_trash_chunk(smp_trash_alloc); + free_trash_chunk(smp_trash); return 0; } # endif diff -Nru haproxy-2.1.11/src/standard.c haproxy-2.1.12/src/standard.c --- haproxy-2.1.11/src/standard.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/standard.c 2021-03-18 13:25:32.000000000 +0000 @@ -4515,8 +4515,10 @@ /* Expected characters after having read an uint: '\0' or '.', * if '.', must not be terminal. */ - if (*s != '\0'&& (*s++ != '.' || s == end)) + if (*s != '\0'&& (*s++ != '.' || s == end)) { + free(n); return 0; + } n = my_realloc2(n, (*sz + 1) * sizeof *n); if (!n) diff -Nru haproxy-2.1.11/src/stats.c haproxy-2.1.12/src/stats.c --- haproxy-2.1.11/src/stats.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/stats.c 2021-03-18 13:25:32.000000000 +0000 @@ -153,6 +153,7 @@ [INF_TOTAL_BYTES_OUT] = { .name = "TotalBytesOut", .desc = "Total number of bytes emitted by current worker process since started" }, [INF_BYTES_OUT_RATE] = { .name = "BytesOutRate", .desc = "Number of bytes emitted by current worker process over the last second" }, [INF_DEBUG_COMMANDS_ISSUED] = { .name = "DebugCommandsIssued", .desc = "Number of debug commands issued on this process (anything > 0 is unsafe)" }, + [INF_BUILD_INFO] = { .name = "Build info", .desc = "Build info" }, }; const struct name_desc stat_fields[ST_F_TOTAL_FIELDS] = { @@ -3414,6 +3415,7 @@ info[INF_NAME] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, PRODUCT_NAME); info[INF_VERSION] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, haproxy_version); + info[INF_BUILD_INFO] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, haproxy_version); info[INF_RELEASE_DATE] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, haproxy_date); info[INF_NBTHREAD] = mkf_u32(FO_CONFIG|FS_SERVICE, global.nbthread); diff -Nru haproxy-2.1.11/src/stick_table.c haproxy-2.1.12/src/stick_table.c --- haproxy-2.1.11/src/stick_table.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/stick_table.c 2021-03-18 13:25:32.000000000 +0000 @@ -2218,7 +2218,7 @@ smp.px = NULL; smp.sess = sess; smp.strm = strm; - if (!smp_fetch_src(NULL, &smp, NULL, NULL)) + if (!smp_fetch_src(empty_arg_list, &smp, NULL, NULL)) return NULL; /* Converts into key. */ @@ -2283,7 +2283,7 @@ smp.px = NULL; smp.sess = sess; smp.strm = strm; - if (!smp_fetch_src(NULL, &smp, NULL, NULL)) + if (!smp_fetch_src(empty_arg_list, &smp, NULL, NULL)) return NULL; /* Converts into key. */ @@ -2815,7 +2815,7 @@ return 0; /* Fetch source address in a sample. */ - if (!smp_fetch_src(NULL, smp, NULL, NULL)) + if (!smp_fetch_src(empty_arg_list, smp, NULL, NULL)) return 0; /* Converts into key. */ diff -Nru haproxy-2.1.11/src/stream.c haproxy-2.1.12/src/stream.c --- haproxy-2.1.11/src/stream.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/stream.c 2021-03-18 13:25:32.000000000 +0000 @@ -3877,17 +3877,22 @@ ptr = (void *)strtoul(args[2], NULL, 0); + thread_isolate(); + /* first, look for the requested stream in the stream table */ list_for_each_entry(strm, &streams, list) { - if (strm == ptr) + if (strm == ptr) { + stream_shutdown(strm, SF_ERR_KILLED); break; + } } + thread_release(); + /* do we have the stream ? */ if (strm != ptr) return cli_err(appctx, "No such session (use 'show sess').\n"); - stream_shutdown(strm, SF_ERR_KILLED); return 1; } diff -Nru haproxy-2.1.11/src/tcp_rules.c haproxy-2.1.12/src/tcp_rules.c --- haproxy-2.1.11/src/tcp_rules.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/tcp_rules.c 2021-03-18 13:25:32.000000000 +0000 @@ -864,7 +864,7 @@ else action_build_list(&tcp_req_cont_keywords, &trash); memprintf(err, - "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s " + "'%s %s' expects 'accept', 'reject', 'capture', 'expect-proxy', 'expect-netscaler-ip', 'track-sc0' ... 'track-sc%d', %s " "in %s '%s' (got '%s').\n", args[0], args[1], MAX_SESS_STKCTR-1, trash.area, proxy_type_str(curpx), diff -Nru haproxy-2.1.11/src/time.c haproxy-2.1.12/src/time.c --- haproxy-2.1.11/src/time.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/time.c 2021-03-18 13:25:32.000000000 +0000 @@ -30,7 +30,7 @@ THREAD_LOCAL struct timeval after_poll; /* system date after leaving poll() */ static THREAD_LOCAL struct timeval tv_offset; /* per-thread time ofsset relative to global time */ -static volatile unsigned long long global_now; /* common date between all threads (32:32) */ +volatile unsigned long long global_now; /* common date between all threads (32:32) */ static THREAD_LOCAL unsigned int iso_time_sec; /* last iso time value for this thread */ static THREAD_LOCAL char iso_time_str[28]; /* ISO time representation of gettimeofday() */ diff -Nru haproxy-2.1.11/src/vars.c haproxy-2.1.12/src/vars.c --- haproxy-2.1.11/src/vars.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/vars.c 2021-03-18 13:25:32.000000000 +0000 @@ -571,9 +571,13 @@ sample_clear_stream(name, scope, smp); } -/* this function fills a sample with the - * variable content. Returns 1 if the sample - * is filled, otherwise it returns 0. +/* This function fills a sample with the variable content. + * + * Keep in mind that a sample content is duplicated by using smp_dup() + * and it therefore uses a pre-allocated trash chunk as returned by + * get_trash_chunk(). + * + * Returns 1 if the sample is filled, otherwise it returns 0. */ int vars_get_by_name(const char *name, size_t len, struct sample *smp) { @@ -592,19 +596,29 @@ return 0; /* Get the variable entry. */ + HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock); var = var_get(vars, name); - if (!var) + if (!var) { + HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock); return 0; + } /* Copy sample. */ smp->data = var->data; - smp->flags = SMP_F_CONST; + smp_dup(smp); + + HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock); return 1; } -/* this function fills a sample with the - * content of the variable described by . Returns 1 - * if the sample is filled, otherwise it returns 0. +/* This function fills a sample with the content of the variable described + * by . + * + * Keep in mind that a sample content is duplicated by using smp_dup() + * and it therefore uses a pre-allocated trash chunk as returned by + * get_trash_chunk(). + * + * Returns 1 if the sample is filled, otherwise it returns 0. */ int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp) { @@ -619,13 +633,18 @@ return 0; /* Get the variable entry. */ + HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock); var = var_get(vars, var_desc->name); - if (!var) + if (!var) { + HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock); return 0; + } /* Copy sample. */ smp->data = var->data; - smp->flags = SMP_F_CONST; + smp_dup(smp); + + HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock); return 1; } diff -Nru haproxy-2.1.11/src/xxhash.c haproxy-2.1.12/src/xxhash.c --- haproxy-2.1.11/src/xxhash.c 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/src/xxhash.c 2021-03-18 13:25:32.000000000 +0000 @@ -39,8 +39,14 @@ // For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected. // If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance. // You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32). +// 32-bit ARM is more annoying, modern cores do support unaligned accesses, but +// not on 64-bit data (the ldrd instructions causes an alignment exception). +// Because of this we need to split the condition for 32 and 64 bit. #if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) # define XXH_USE_UNALIGNED_ACCESS 1 +# if !defined(__arm__) +# define XXH_USE_UNALIGNED_ACCESS64 1 +# endif #endif // XXH_ACCEPT_NULL_INPUT_POINTER : @@ -118,6 +124,12 @@ # define _PACKED #endif +#if defined(__GNUC__) && !defined(XXH_USE_UNALIGNED_ACCESS64) +# define _PACKED64 __attribute__ ((packed)) +#else +# define _PACKED64 +#endif + #if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) # ifdef __IBMC__ # pragma pack(1) @@ -133,7 +145,7 @@ typedef struct _U64_S { U64 v; -} _PACKED U64_S; +} _PACKED64 U64_S; #if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__) # pragma pack(pop) @@ -479,7 +491,7 @@ #else XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; -# if !defined(XXH_USE_UNALIGNED_ACCESS) +# if !defined(XXH_USE_UNALIGNED_ACCESS64) if ((((size_t)input) & 7)==0) // Input is aligned, let's leverage the speed advantage { if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) diff -Nru haproxy-2.1.11/SUBVERS haproxy-2.1.12/SUBVERS --- haproxy-2.1.11/SUBVERS 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/SUBVERS 2021-03-18 13:25:32.000000000 +0000 @@ -1,2 +1,2 @@ --9da7aab +-529bae5 diff -Nru haproxy-2.1.11/VERDATE haproxy-2.1.12/VERDATE --- haproxy-2.1.11/VERDATE 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/VERDATE 2021-03-18 13:25:32.000000000 +0000 @@ -1,2 +1,2 @@ -2021-01-08 21:24:59 +0100 -2021/01/08 +2021-03-18 14:25:32 +0100 +2021/03/18 diff -Nru haproxy-2.1.11/VERSION haproxy-2.1.12/VERSION --- haproxy-2.1.11/VERSION 2021-01-08 20:24:59.000000000 +0000 +++ haproxy-2.1.12/VERSION 2021-03-18 13:25:32.000000000 +0000 @@ -1 +1 @@ -2.1.11 +2.1.12