diff -Nru haproxy-2.2.8/CHANGELOG haproxy-2.2.9/CHANGELOG --- haproxy-2.2.8/CHANGELOG 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/CHANGELOG 2021-02-06 16:02:45.000000000 +0000 @@ -1,6 +1,59 @@ ChangeLog : =========== +2021/02/06 : 2.2.9 + - BUG/MINOR: init: Use a dynamic buffer to set HAPROXY_CFGFILES env variable + - MINOR: config: Add failifnotcap() to emit an alert on proxy capabilities + - MINOR: server: Forbid server definitions in frontend sections + - BUG/MINOR: threads: Fixes the number of possible cpus report for Mac. + - MINOR: peers: Add traces for peer control messages. + - BUG/MINOR: dns: SRV records ignores duplicated AR records (v2) + - BUILD: peers: fix build warning about unused variable + - BUG/MEDIUM: stats: add missing INF_BUILD_INFO definition + - BUG/MINOR: peers: Possible appctx pointer dereference. + - MINOR: build: discard echoing in help target + - 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 + - DOC: Improve documentation of the various hdr() fetches + - BUG/MEDIUM: filters/htx: Fix data forwarding when payload length is unknown + - BUG/MINOR: config: fix leak on proxy.conn_src.bind_hdr_name + - BUG/MINOR: ssl: init tmp chunk correctly in ssl_sock_load_sctl_from_file() + - MINOR: contrib: Make the wireshark peers dissector compile for more distribs. + - CLEANUP: tools: make resolve_sym_name() take a const pointer + - CLEANUP: cli: make "show fd" use a const connection to access other fields + - MINOR: cli: make "show fd" also report the xprt and xprt_ctx + - MINOR: xprt: add a new show_fd() helper to complete some "show fd" dumps. + - MINOR: ssl: provide a "show fd" helper to report important SSL information + - MINOR: xprt/mux: export all *_io_cb functions so that "show fd" resolves them + - MINOR: mux-h2: make the "show fd" helper also decode the h2s subscriber when known + - MINOR: mux-h1: make the "show fd" helper also decode the h1s subscriber when known + - MINOR: mux-fcgi: make the "show fd" helper also decode the fstrm subscriber when known + - MINOR: cli: give the show_fd helpers the ability to report a suspicious entry + - MINOR: cli/show_fd: report some easily detectable suspicious states + - MINOR: ssl/show_fd: report some FDs as suspicious when possible + - MINOR: mux-h2/show_fd: report as suspicious an entry with too many calls + - MINOR: mux-h1/show_fd: report as suspicious an entry with too many calls + - MINOR: h1: Raise the chunk size limit up to (2^52 - 1) + - DOC: management: fix "show resolvers" alphabetical ordering + - BUG/MINOR: stick-table: Always call smp_fetch_src() with a valid arg list + - BUG/MEDIUM: ssl/cli: abort ssl cert is freeing the old store + - BUG/MEDIUM: ssl: check a connection's status before computing a handshake + - BUG/MINOR: xxhash: make sure armv6 uses memcpy() + - 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 + - MINOR: cli/show_fd: report local and report ports when known + - MINOR: config: Deprecate and ignore tune.chksize global option + - BUG/MAJOR: connection: reset conn->owner when detaching from session list + - BUG/MEDIUM: lists: Lock the element while we check if it is in a list. + - MINOR: task: remove __tasklet_remove_from_tasklet_list() + - BUG/MEDIUM: task: close a possible data race condition on a tasklet's list link + - DOC: fix "smp_size" vs "sample_size" in "log" directive arguments + - BUG/MEDIUM: tcpcheck: Don't destroy connection in the wake callback context + 2021/01/13 : 2.2.8 - MINOR: reg-tests: add a way to add service dependency - BUG/MINOR: sample: check alloc_trash_chunk return value in concat() diff -Nru haproxy-2.2.8/contrib/wireshark-dissectors/peers/Makefile haproxy-2.2.9/contrib/wireshark-dissectors/peers/Makefile --- haproxy-2.2.8/contrib/wireshark-dissectors/peers/Makefile 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/contrib/wireshark-dissectors/peers/Makefile 2021-02-06 16:02:45.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.2.8/contrib/wireshark-dissectors/peers/packet-happp.c haproxy-2.2.9/contrib/wireshark-dissectors/peers/packet-happp.c --- haproxy-2.2.8/contrib/wireshark-dissectors/peers/packet-happp.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/contrib/wireshark-dissectors/peers/packet-happp.c 2021-02-06 16:02:45.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.2.8/contrib/wireshark-dissectors/peers/README haproxy-2.2.9/contrib/wireshark-dissectors/peers/README --- haproxy-2.2.8/contrib/wireshark-dissectors/peers/README 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/contrib/wireshark-dissectors/peers/README 2021-02-06 16:02:45.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.2.8/debian/changelog haproxy-2.2.9/debian/changelog --- haproxy-2.2.8/debian/changelog 2021-01-14 10:48:52.000000000 +0000 +++ haproxy-2.2.9/debian/changelog 2021-02-16 15:15:35.000000000 +0000 @@ -1,3 +1,17 @@ +haproxy (2.2.9-1build1) hirsute; urgency=medium + + * No change rebuild with fixed ownership. + + -- Dimitri John Ledkov Tue, 16 Feb 2021 15:15:35 +0000 + +haproxy (2.2.9-1) unstable; urgency=medium + + * New upstream release. + - BUG/MAJOR: connection: reset conn->owner when detaching from session + list + + -- Vincent Bernat Sat, 06 Feb 2021 18:52:20 +0100 + haproxy (2.2.8-1) unstable; urgency=medium * New upstream release. diff -Nru haproxy-2.2.8/debian/control haproxy-2.2.9/debian/control --- haproxy-2.2.8/debian/control 2021-01-14 10:48:52.000000000 +0000 +++ haproxy-2.2.9/debian/control 2021-02-16 15:15:35.000000000 +0000 @@ -1,7 +1,8 @@ Source: haproxy Section: net Priority: optional -Maintainer: Debian HAProxy Maintainers +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Debian HAProxy Maintainers Uploaders: Apollon Oikonomopoulos , Prach Pongpanich , Vincent Bernat diff -Nru haproxy-2.2.8/doc/configuration.txt haproxy-2.2.9/doc/configuration.txt --- haproxy-2.2.8/doc/configuration.txt 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/doc/configuration.txt 2021-02-06 16:02:45.000000000 +0000 @@ -4,7 +4,7 @@ ---------------------- version 2.2 willy tarreau - 2021/01/13 + 2021/02/06 This document covers the configuration language as implemented in the version @@ -1210,7 +1210,7 @@ This option will also set the HAPROXY_LOCALPEER environment variable. See also "-L" in the management guide and "peers" section below. -log
[len ] [format ] [sample :] +log
[len ] [format ] [sample :] [max level [min level]] Adds a global syslog server. Several global servers can be defined. They will receive logs for starts and exits, as well as all logs from proxies @@ -2087,12 +2087,8 @@ value set using this parameter will automatically be rounded up to the next multiple of 8 on 32-bit machines and 16 on 64-bit machines. -tune.chksize - Sets the check buffer size to this size (in bytes). Higher values may help - find string or regex patterns in very large pages, though doing so may imply - more memory and CPU usage. The default value is 16384 and can be changed at - build time. It is not recommended to change this value, but to use better - checks whenever possible. +tune.chksize (deprecated) + This option is deprecated and ignored. tune.comp.maxlevel Sets the maximum compression level. The compression level affects CPU @@ -2623,7 +2619,7 @@ enable This re-enables a disabled peers section which was previously disabled. -log
[len ] [format ] [sample :] +log
[len ] [format ] [sample :] [ []] "peers" sections support the same "log" keyword as for the proxies to log information about the "peers" listener. See "log" option for proxies for @@ -5083,7 +5079,7 @@ considered invalid if the body contains the string. It is important to note that the responses will be limited to a certain size - defined by the global "tune.chksize" option, which defaults to 16384 bytes. + defined by the global "tune.bufsize" option, which defaults to 16384 bytes. Thus, too large responses may not contain the mandatory pattern when using "string" or "rstring". If a large response is absolutely required, it is possible to change the default max size by setting the global variable. @@ -6872,7 +6868,7 @@ log global -log
[len ] [format ] [sample :] +log
[len ] [format ] [sample :] [ []] no log Enable per-instance logging of events and traffic. @@ -10892,7 +10888,7 @@ buffer. It is important to note that the responses will be limited to a certain size - defined by the global "tune.chksize" option, which defaults to 16384 bytes. + defined by the global "tune.bufsize" option, which defaults to 16384 bytes. Thus, too large responses may not contain the mandatory pattern when using "string", "rstring" or binary. If a large response is absolutely required, it is possible to change the default max size by setting the global variable. @@ -10923,7 +10919,7 @@ See also : "option tcp-check", "tcp-check connect", "tcp-check send", - "tcp-check send-binary", "http-check expect", tune.chksize + "tcp-check send-binary", "http-check expect", tune.bufsize tcp-check send [comment ] @@ -10949,7 +10945,7 @@ tcp-check expect string role:master See also : "option tcp-check", "tcp-check connect", "tcp-check expect", - "tcp-check send-binary", tune.chksize + "tcp-check send-binary", tune.bufsize tcp-check send-binary [comment ] @@ -10977,7 +10973,7 @@ See also : "option tcp-check", "tcp-check connect", "tcp-check expect", - "tcp-check send", tune.chksize + "tcp-check send", tune.bufsize tcp-check set-var() @@ -17993,33 +17989,44 @@ unambiguously apply to the request headers. req.fhdr([,]) : string - This extracts the last occurrence of header in an HTTP request. When - used from an ACL, all occurrences are iterated over until a match is found. + This returns the full value of the last occurrence of header in an + HTTP request. It differs from req.hdr() in that any commas present in the + value are returned and are not used as delimiters. This is sometimes useful + with headers such as User-Agent. + + When used from an ACL, all occurrences are iterated over until a match is + found. + Optionally, a specific occurrence might be specified as a position number. Positive values indicate a position from the first occurrence, with 1 being the first one. Negative values indicate positions relative to the last one, - with -1 being the last one. It differs from req.hdr() in that any commas - present in the value are returned and are not used as delimiters. This is - sometimes useful with headers such as User-Agent. + with -1 being the last one. req.fhdr_cnt([]) : integer Returns an integer value representing the number of occurrences of request header field name , or the total number of header fields if is - not specified. Contrary to its req.hdr_cnt() cousin, this function returns - the number of full line headers and does not stop on commas. + not specified. Like req.fhdr() it differs from res.hdr_cnt() by not splitting + headers at commas. req.hdr([[,]]) : string - This extracts the last occurrence of header in an HTTP request. When - used from an ACL, all occurrences are iterated over until a match is found. + This returns the last comma-separated value of the header in an HTTP + request. The fetch considers any comma as a delimiter for distinct values. + This is useful if you need to process headers that are defined to be a list + of values, such as Accept, or X-Forwarded-For. If full-line headers are + desired instead, use req.fhdr(). Please carefully check RFC 7231 to know how + certain headers are supposed to be parsed. Also, some of them are case + insensitive (e.g. Connection). + + When used from an ACL, all occurrences are iterated over until a match is + found. + Optionally, a specific occurrence might be specified as a position number. Positive values indicate a position from the first occurrence, with 1 being the first one. Negative values indicate positions relative to the last one, - with -1 being the last one. A typical use is with the X-Forwarded-For header - once converted to IP, associated with an IP stick-table. The function - considers any comma as a delimiter for distinct values. If full-line headers - are desired instead, use req.fhdr(). Please carefully check RFC7231 to know - how certain headers are supposed to be parsed. Also, some of them are case - insensitive (e.g. Connection). + with -1 being the last one. + + A typical use is with the X-Forwarded-For header once converted to IP, + associated with an IP stick-table. ACL derivatives : hdr([[,]]) : exact string match @@ -18035,34 +18042,36 @@ hdr_cnt([
]) : integer (deprecated) Returns an integer value representing the number of occurrences of request header field name , or the total number of header field values if - is not specified. It is important to remember that one header line may - count as several headers if it has several values. The function considers any - comma as a delimiter for distinct values. If full-line headers are desired - instead, req.fhdr_cnt() should be used instead. With ACLs, it can be used to - detect presence, absence or abuse of a specific header, as well as to block - request smuggling attacks by rejecting requests which contain more than one - of certain headers. See "req.hdr" for more information on header matching. + is not specified. Like req.hdr() it counts each comma separated + part of the header's value. If counting of full-line headers is desired, + then req.fhdr_cnt() should be used instead. + + With ACLs, it can be used to detect presence, absence or abuse of a specific + header, as well as to block request smuggling attacks by rejecting requests + which contain more than one of certain headers. + + Refer to req.hdr() for more information on header matching. req.hdr_ip([[,]]) : ip hdr_ip([[,]]) : ip (deprecated) This extracts the last occurrence of header in an HTTP request, converts it to an IPv4 or IPv6 address and returns this address. When used with ACLs, all occurrences are checked, and if is omitted, every value - of every header is checked. Optionally, a specific occurrence might be - specified as a position number. Positive values indicate a position from the - first occurrence, with 1 being the first one. Negative values indicate - positions relative to the last one, with -1 being the last one. A typical use - is with the X-Forwarded-For and X-Client-IP headers. + of every header is checked. + + The parameter is processed as with req.hdr(). + + A typical use is with the X-Forwarded-For and X-Client-IP headers. req.hdr_val([[,]]) : integer hdr_val([[,]]) : integer (deprecated) This extracts the last occurrence of header in an HTTP request, and converts it to an integer value. When used with ACLs, all occurrences are checked, and if is omitted, every value of every header is checked. - Optionally, a specific occurrence might be specified as a position number. - Positive values indicate a position from the first occurrence, with 1 being - the first one. Negative values indicate positions relative to the last one, - with -1 being the last one. A typical use is with the X-Forwarded-For header. + + The parameter is processed as with req.hdr(). + + A typical use is with the X-Forwarded-For header. req.hdrs : string Returns the current request headers as string including the last empty line @@ -18194,22 +18203,25 @@ res.body : binary This returns the HTTP response's available body as a block of data. Unlike the request side, there is no directive to wait for the response's body. This - sample fetch is really useful (and usable) in the health-check context. It - may be used in tcp-check based expect rules. + sample fetch is really useful (and usable) in the health-check context. + + It may be used in tcp-check based expect rules. res.body_len : integer This returns the length of the HTTP response available body in bytes. Unlike the request side, there is no directive to wait for the response's body. This - sample fetch is really useful (and usable) in the health-check context. It - may be used in tcp-check based expect rules. + sample fetch is really useful (and usable) in the health-check context. + + It may be used in tcp-check based expect rules. res.body_size : integer This returns the advertised length of the HTTP response body in bytes. It will represent the advertised Content-Length header, or the size of the available data in case of chunked encoding. Unlike the request side, there is no directive to wait for the response body. This sample fetch is really - useful (and usable) in the health-check context. It may be used in tcp-check - based expect rules. + useful (and usable) in the health-check context. + + It may be used in tcp-check based expect rules. res.comp : boolean Returns the boolean "true" value if the response has been compressed by @@ -18225,8 +18237,9 @@ scook([]) : string (deprecated) This extracts the last occurrence of the cookie name on a "Set-Cookie" header line from the response, and returns its value as string. If no name is - specified, the first cookie value is returned. It may be used in tcp-check - based expect rules. + specified, the first cookie value is returned. + + It may be used in tcp-check based expect rules. ACL derivatives : scook([] : exact string match @@ -18235,46 +18248,47 @@ scook_cnt([]) : integer (deprecated) Returns an integer value representing the number of occurrences of the cookie in the response, or all cookies if is not specified. This is - mostly useful when combined with ACLs to detect suspicious responses. It may - be used in tcp-check based expect rules. + mostly useful when combined with ACLs to detect suspicious responses. + + It may be used in tcp-check based expect rules. res.cook_val([]) : integer scook_val([]) : integer (deprecated) This extracts the last occurrence of the cookie name on a "Set-Cookie" header line from the response, and converts its value to an integer which is - returned. If no name is specified, the first cookie value is returned. It may - be used in tcp-check based expect rules. + returned. If no name is specified, the first cookie value is returned. + + It may be used in tcp-check based expect rules. res.fhdr([[,]]) : string - This extracts the last occurrence of header in an HTTP response, or of - the last header if no is specified. Optionally, a specific occurrence - might be specified as a position number. Positive values indicate a position - from the first occurrence, with 1 being the first one. Negative values - indicate positions relative to the last one, with -1 being the last one. It - differs from res.hdr() in that any commas present in the value are returned - and are not used as delimiters. If this is not desired, the res.hdr() fetch - should be used instead. This is sometimes useful with headers such as Date or - Expires. It may be used in tcp-check based expect rules. + This fetch works like the req.fhdr() fetch with the difference that it acts + on the headers within an HTTP response. + + Like req.fhdr() the res.fhdr() fetch returns full values. If the header is + defined to be a list you should use res.hdr(). + + This fetch is sometimes useful with headers such as Date or Expires. + + It may be used in tcp-check based expect rules. res.fhdr_cnt([]) : integer - Returns an integer value representing the number of occurrences of response - header field name , or the total number of header fields if is - not specified. Contrary to its res.hdr_cnt() cousin, this function returns - the number of full line headers and does not stop on commas. If this is not - desired, the res.hdr_cnt() fetch should be used instead. It may be used in - tcp-check based expect rules. + This fetch works like the req.fhdr_cnt() fetch with the difference that it + acts on the headers within an HTTP response. + + Like req.fhdr_cnt() the res.fhdr_cnt() fetch acts on full values. If the + header is defined to be a list you should use res.hdr_cnt(). + + It may be used in tcp-check based expect rules. res.hdr([[,]]) : string shdr([[,]]) : string (deprecated) - This extracts the last occurrence of header in an HTTP response, or of - the last header if no is specified. Optionally, a specific occurrence - might be specified as a position number. Positive values indicate a position - from the first occurrence, with 1 being the first one. Negative values - indicate positions relative to the last one, with -1 being the last one. This - can be useful to learn some data into a stick-table. The function considers - any comma as a delimiter for distinct values. If this is not desired, the - res.fhdr() fetch should be used instead. It may be used in tcp-check based - expect rules. + This fetch works like the req.hdr() fetch with the difference that it acts + on the headers within an HTTP response. + + Like req.hdr() the res.hdr() fetch considers the comma to be a delimeter. If + this is not desired res.fhdr() should be used. + + It may be used in tcp-check based expect rules. ACL derivatives : shdr([[,]]) : exact string match @@ -18288,45 +18302,47 @@ res.hdr_cnt([]) : integer shdr_cnt([]) : integer (deprecated) - Returns an integer value representing the number of occurrences of response - header field name , or the total number of header fields if is - not specified. The function considers any comma as a delimiter for distinct - values. If this is not desired, the res.fhdr_cnt() fetch should be used - instead. It may be used in tcp-check based expect rules. + This fetch works like the req.hdr_cnt() fetch with the difference that it + acts on the headers within an HTTP response. + + Like req.hdr_cnt() the res.hdr_cnt() fetch considers the comma to be a + delimeter. If this is not desired res.fhdr_cnt() should be used. + + It may be used in tcp-check based expect rules. res.hdr_ip([[,]]) : ip shdr_ip([[,]]) : ip (deprecated) - This extracts the last occurrence of header in an HTTP response, - convert it to an IPv4 or IPv6 address and returns this address. Optionally, a - specific occurrence might be specified as a position number. Positive values - indicate a position from the first occurrence, with 1 being the first one. - Negative values indicate positions relative to the last one, with -1 being - the last one. This can be useful to learn some data into a stick table. It - may be used in tcp-check based expect rules. + This fetch works like the req.hdr_ip() fetch with the difference that it + acts on the headers within an HTTP response. + + This can be useful to learn some data into a stick table. + + It may be used in tcp-check based expect rules. res.hdr_names([]) : string This builds a string made from the concatenation of all header names as they appear in the response when the rule is evaluated. The default delimiter is the comma (',') but it may be overridden as an optional argument . In - this case, only the first character of is considered. It may be used - in tcp-check based expect rules. + this case, only the first character of is considered. + + It may be used in tcp-check based expect rules. res.hdr_val([[,]]) : integer shdr_val([[,]]) : integer (deprecated) - This extracts the last occurrence of header in an HTTP response, and - converts it to an integer value. Optionally, a specific occurrence might be - specified as a position number. Positive values indicate a position from the - first occurrence, with 1 being the first one. Negative values indicate - positions relative to the last one, with -1 being the last one. This can be - useful to learn some data into a stick table. It may be used in tcp-check - based expect rules. + This fetch works like the req.hdr_val() fetch with the difference that it + acts on the headers within an HTTP response. + + This can be useful to learn some data into a stick table. + + It may be used in tcp-check based expect rules. res.hdrs : string Returns the current response headers as string including the last empty line separating headers from the request body. The last empty line can be used to detect a truncated header block. This sample fetch is useful for some SPOE - headers analyzers and for advanced logging. It may also be used in tcp-check - based expect rules. + headers analyzers and for advanced logging. + + It may also be used in tcp-check based expect rules. res.hdrs_bin : binary Returns the current response headers contained in preparsed binary form. This @@ -18345,8 +18361,9 @@ res.ver : string resp_ver : string (deprecated) Returns the version string from the HTTP response, for example "1.1". This - can be useful for logs, but is mostly there for ACL. It may be used in - tcp-check based expect rules. + can be useful for logs, but is mostly there for ACL. + + It may be used in tcp-check based expect rules. ACL derivatives : resp_ver : exact string match @@ -18363,8 +18380,9 @@ status : integer Returns an integer containing the HTTP status code in the HTTP response, for example, 302. It is mostly used within ACLs and integer ranges, for example, - to remove any Location header if the response is not a 3xx. It may be used in - tcp-check based expect rules. + to remove any Location header if the response is not a 3xx. + + It may be used in tcp-check based expect rules. unique-id : string Returns the unique-id attached to the request. The directive @@ -20312,7 +20330,7 @@ log-stderr global log-stderr
[len ] [format ] - [sample :] [ []] + [sample :] [ []] Enable logging of STDERR messages reported by the FastCGI application. See "log" keyword in section 4.2 for details. It is an optional setting. By diff -Nru haproxy-2.2.8/doc/management.txt haproxy-2.2.9/doc/management.txt --- haproxy-2.2.8/doc/management.txt 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/doc/management.txt 2021-02-06 16:02:45.000000000 +0000 @@ -2111,7 +2111,10 @@ listener's state and its frontend are reported. There is no point in using this command without a good knowledge of the internals. It's worth noting that the output format may evolve over time so this output must not be parsed - by tools designed to be durable. + by tools designed to be durable. Some internal structure states may look + suspicious to the function listing them, in this case the output line will be + suffixed with an exclamation mark ('!'). This may help find a starting point + when trying to diagnose an incident. show info [typed|json] [desc] Dump info about haproxy status on current process. If "typed" is passed as an @@ -2294,6 +2297,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 conn [] Dump the current and idle connections state of the servers belonging to the designated backend (or all backends if none specified). A backend name or @@ -2634,25 +2656,6 @@ ecdsa.pem:3 [verify none allow-0rtt ssl-min-ver TLSv1.0 ssl-max-ver TLSv1.3] localhost !www.test1.com ecdsa.pem:4 [verify none allow-0rtt ssl-min-ver TLSv1.0 ssl-max-ver TLSv1.3] -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.2.8/include/haproxy/cfgparse.h haproxy-2.2.9/include/haproxy/cfgparse.h --- haproxy-2.2.8/include/haproxy/cfgparse.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/cfgparse.h 2021-02-06 16:02:45.000000000 +0000 @@ -140,6 +140,31 @@ return 0; } +/* + * Sends an alert if proxy does not have at least one of the + * capabilities in . An optional 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. + */ +static inline 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; +} + /* simplified way to define a section parser */ #define REGISTER_CONFIG_SECTION(name, parse, post) \ INITCALL3(STG_REGISTER, cfg_register_section, (name), (parse), (post)) diff -Nru haproxy-2.2.8/include/haproxy/check-t.h haproxy-2.2.9/include/haproxy/check-t.h --- haproxy-2.2.8/include/haproxy/check-t.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/check-t.h 2021-02-06 16:02:45.000000000 +0000 @@ -52,6 +52,7 @@ #define CHK_ST_PORT_MISS 0x0020 /* check can't be send because no port is configured to run it */ #define CHK_ST_IN_ALLOC 0x0040 /* check blocked waiting for input buffer allocation */ #define CHK_ST_OUT_ALLOC 0x0080 /* check blocked waiting for output buffer allocation */ +#define CHK_ST_CLOSE_CONN 0x0100 /* check is waiting that the connection gets closed */ /* check status */ enum healthcheck_status { diff -Nru haproxy-2.2.8/include/haproxy/connection-t.h haproxy-2.2.9/include/haproxy/connection-t.h --- haproxy-2.2.8/include/haproxy/connection-t.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/connection-t.h 2021-02-06 16:02:45.000000000 +0000 @@ -360,6 +360,7 @@ int (*unsubscribe)(struct connection *conn, void *xprt_ctx, int event_type, struct wait_event *es); /* Unsubscribe from events */ int (*remove_xprt)(struct connection *conn, void *xprt_ctx, void *toremove_ctx, const struct xprt_ops *newops, void *newctx); /* Remove an xprt from the connection, used by temporary xprt such as the handshake one */ int (*add_xprt)(struct connection *conn, void *xprt_ctx, void *toadd_ctx, const struct xprt_ops *toadd_ops, void **oldxprt_ctx, const struct xprt_ops **oldxprt_ops); /* Add a new XPRT as the new xprt, and return the old one */ + int (*show_fd)(struct buffer *, const struct connection *, const void *ctx); /* append some data about xprt for "show fd"; returns non-zero if suspicious */ }; /* mux_ops describes the mux operations, which are to be performed at the @@ -382,7 +383,7 @@ struct conn_stream *(*attach)(struct connection *, struct session *sess); /* Create and attach a conn_stream to an outgoing connection */ const struct conn_stream *(*get_first_cs)(const struct connection *); /* retrieves any valid conn_stream from this connection */ void (*detach)(struct conn_stream *); /* Detach a conn_stream from an outgoing connection, when the request is done */ - void (*show_fd)(struct buffer *, struct connection *); /* append some data about connection into chunk for "show fd" */ + int (*show_fd)(struct buffer *, struct connection *); /* append some data about connection into chunk for "show fd"; returns non-zero if suspicious */ int (*subscribe)(struct conn_stream *cs, int event_type, struct wait_event *es); /* Subscribe to events, such as "being able to send" */ int (*unsubscribe)(struct conn_stream *cs, int event_type, struct wait_event *es); /* Unsubscribe from events */ int (*avail_streams)(struct connection *conn); /* Returns the number of streams still available for a connection */ diff -Nru haproxy-2.2.8/include/haproxy/filters-t.h haproxy-2.2.9/include/haproxy/filters-t.h --- haproxy-2.2.8/include/haproxy/filters-t.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/filters-t.h 2021-02-06 16:02:45.000000000 +0000 @@ -33,6 +33,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 */ struct http_msg; diff -Nru haproxy-2.2.8/include/haproxy/global-t.h haproxy-2.2.9/include/haproxy/global-t.h --- haproxy-2.2.8/include/haproxy/global-t.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/global-t.h 2021-02-06 16:02:45.000000000 +0000 @@ -150,7 +150,6 @@ int client_rcvbuf; /* set client rcvbuf to this value if not null */ int server_sndbuf; /* set server sndbuf to this value if not null */ int server_rcvbuf; /* set server rcvbuf to this value if not null */ - int chksize; /* check buffer size in bytes, defaults to BUFSIZE */ int pipesize; /* pipe size in bytes, system defaults if zero */ int max_http_hdr; /* max number of HTTP headers, use MAX_HTTP_HDR if zero */ int requri_len; /* max len of request URI, use REQURI_LEN if zero */ diff -Nru haproxy-2.2.8/include/haproxy/h1.h haproxy-2.2.9/include/haproxy/h1.h --- haproxy-2.2.8/include/haproxy/h1.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/h1.h 2021-02-06 16:02:45.000000000 +0000 @@ -239,12 +239,12 @@ * start of the body and the start of the data. Note: this function is designed * to parse wrapped CRLF at the end of the buffer. */ -static inline int h1_parse_chunk_size(const struct buffer *buf, int start, int stop, unsigned int *res) +static inline int h1_parse_chunk_size(const struct buffer *buf, int start, int stop, uint64_t *res) { const char *ptr = b_peek(buf, start); const char *ptr_old = ptr; const char *end = b_wrap(buf); - unsigned int chunk = 0; + uint64_t chunk = 0; stop -= start; // bytes left start = stop; // bytes to transfer @@ -262,9 +262,13 @@ break; if (unlikely(++ptr >= end)) ptr = b_orig(buf); - if (unlikely(chunk & 0xF8000000)) /* integer overflow will occur if result >= 2GB */ - goto error; chunk = (chunk << 4) + c; + if (unlikely(chunk & 0xF0000000000000)) { + /* Don't get more than 13 hexa-digit (2^52 - 1) to never fed possibly + * bogus values from languages that use floats for their integers + */ + goto error; + } stop--; } diff -Nru haproxy-2.2.8/include/haproxy/list.h haproxy-2.2.9/include/haproxy/list.h --- haproxy-2.2.8/include/haproxy/list.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/list.h 2021-02-06 16:02:45.000000000 +0000 @@ -230,8 +230,8 @@ int _ret = 0; \ struct mt_list *lh = (_lh), *el = (_el); \ while (1) { \ - struct mt_list *n; \ - struct mt_list *p; \ + 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; \ @@ -241,10 +241,29 @@ __ha_barrier_store(); \ continue; \ } \ - if ((el)->next != (el) || (el)->prev != (el)) { \ - (n)->prev = p; \ - (lh)->next = n; \ + 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(); \ + lh->next = n; \ __ha_barrier_store(); \ + if (n2 == MT_LIST_BUSY) \ + continue; \ + break; \ + } \ + 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; \ @@ -270,8 +289,8 @@ int _ret = 0; \ struct mt_list *lh = (_lh), *el = (_el); \ while (1) { \ - struct mt_list *n; \ - struct mt_list *p; \ + 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; \ @@ -281,10 +300,29 @@ __ha_barrier_store(); \ continue; \ } \ - if ((el)->next != (el) || (el)->prev != (el)) { \ + 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; \ - (lh)->prev = p; \ __ha_barrier_store(); \ + lh->prev = p; \ + __ha_barrier_store(); \ + if (n2 == MT_LIST_BUSY) \ + continue; \ + break; \ + } \ + p2 = _HA_ATOMIC_XCHG(&el->prev, MT_LIST_BUSY); \ + if (p2 != el) { \ + if (p2 != MT_LIST_BUSY) \ + el->prev = p2; \ + p->next = n; \ + el->next = el; \ + __ha_barrier_store(); \ + lh->prev = p; \ + __ha_barrier_store(); \ + if (p2 == MT_LIST_BUSY) \ + continue; \ break; \ } \ (el)->next = n; \ diff -Nru haproxy-2.2.8/include/haproxy/session.h haproxy-2.2.9/include/haproxy/session.h --- haproxy-2.2.8/include/haproxy/session.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/session.h 2021-02-06 16:02:45.000000000 +0000 @@ -77,8 +77,19 @@ static inline void session_unown_conn(struct session *sess, struct connection *conn) { struct sess_srv_list *srv_list = NULL; - LIST_DEL(&conn->session_list); - LIST_INIT(&conn->session_list); + + /* WT: this currently is a workaround for an inconsistency between + * the link status of the connection in the session list and the + * connection's owner. This should be removed as soon as all this + * is addressed. Right now it's possible to enter here with a non-null + * conn->owner that points to a dead session, but in this case the + * element is not linked. + */ + if (!LIST_ADDED(&conn->session_list)) + return; + + LIST_DEL_INIT(&conn->session_list); + conn->owner = NULL; list_for_each_entry(srv_list, &sess->srv_list, srv_list) { if (srv_list->target == conn->target) { if (LIST_ISEMPTY(&srv_list->conn_list)) { diff -Nru haproxy-2.2.8/include/haproxy/task.h haproxy-2.2.9/include/haproxy/task.h --- haproxy-2.2.8/include/haproxy/task.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/task.h 2021-02-06 16:02:45.000000000 +0000 @@ -323,43 +323,50 @@ } /* schedules tasklet to run onto thread or the current thread if - * is negative. + * is negative. Note that it is illegal to wakeup a foreign tasklet if + * its tid is negative and it is illegal to self-assign a tasklet that was + * at least once scheduled on a specific thread. */ static inline void tasklet_wakeup_on(struct tasklet *tl, int thr) { + unsigned short state = tl->state; + + do { + /* do nothing if someone else already added it */ + if (state & TASK_IN_LIST) + return; + } while (!_HA_ATOMIC_CAS(&tl->state, &state, state | TASK_IN_LIST)); + + /* at this pint we're the first ones to add this task to the list */ + if (likely(thr < 0)) { /* this tasklet runs on the caller thread */ - if (LIST_ISEMPTY(&tl->list)) { - if (tl->state & TASK_SELF_WAKING) { - LIST_ADDQ(&sched->tasklets[TL_BULK], &tl->list); - sched->tl_class_mask |= 1 << TL_BULK; - } - else if ((struct task *)tl == sched->current) { - _HA_ATOMIC_OR(&tl->state, TASK_SELF_WAKING); - LIST_ADDQ(&sched->tasklets[TL_BULK], &tl->list); - sched->tl_class_mask |= 1 << TL_BULK; - } - else if (sched->current_queue < 0) { - LIST_ADDQ(&sched->tasklets[TL_URGENT], &tl->list); - sched->tl_class_mask |= 1 << TL_URGENT; - } - else { - LIST_ADDQ(&sched->tasklets[sched->current_queue], &tl->list); - sched->tl_class_mask |= 1 << sched->current_queue; - } - - _HA_ATOMIC_ADD(&tasks_run_queue, 1); + if (tl->state & TASK_SELF_WAKING) { + LIST_ADDQ(&sched->tasklets[TL_BULK], &tl->list); + sched->tl_class_mask |= 1 << TL_BULK; + } + else if ((struct task *)tl == sched->current) { + _HA_ATOMIC_OR(&tl->state, TASK_SELF_WAKING); + LIST_ADDQ(&sched->tasklets[TL_BULK], &tl->list); + sched->tl_class_mask |= 1 << TL_BULK; + } + else if (sched->current_queue < 0) { + LIST_ADDQ(&sched->tasklets[TL_URGENT], &tl->list); + sched->tl_class_mask |= 1 << TL_URGENT; + } + else { + LIST_ADDQ(&sched->tasklets[sched->current_queue], &tl->list); + sched->tl_class_mask |= 1 << sched->current_queue; } } else { - /* this tasklet runs on a specific thread */ - if (MT_LIST_ADDQ(&task_per_thread[thr].shared_tasklet_list, (struct mt_list *)&tl->list) == 1) { - _HA_ATOMIC_ADD(&tasks_run_queue, 1); - if (sleeping_thread_mask & (1UL << thr)) { - _HA_ATOMIC_AND(&sleeping_thread_mask, ~(1UL << thr)); - wake_thread(thr); - } + /* this tasklet runs on a specific thread. */ + MT_LIST_ADDQ_NOCHECK(&task_per_thread[thr].shared_tasklet_list, (struct mt_list *)&tl->list); + if (sleeping_thread_mask & (1UL << thr)) { + _HA_ATOMIC_AND(&sleeping_thread_mask, ~(1UL << thr)); + wake_thread(thr); } } + _HA_ATOMIC_ADD(&tasks_run_queue, 1); } /* schedules tasklet to run onto the thread designated by tl->tid, which @@ -379,22 +386,18 @@ LIST_ADDQ(list, &tl->list); } -/* Remove the tasklet from the tasklet list. The tasklet MUST already be there. - * If unsure, use tasklet_remove_from_tasklet_list() instead. If used with a - * plain task, the caller must update the task_list_size. - * This should only be used by the thread that owns the tasklet, any other - * thread should use tasklet_cancel(). +/* Try to remove a tasklet from the list. This call is inherently racy and may + * only be performed on the thread that was supposed to dequeue this tasklet. + * This way it is safe to call MT_LIST_DEL without first removing the + * TASK_IN_LIST bit, which must absolutely be removed afterwards in case + * another thread would want to wake this tasklet up in parallel. */ -static inline void __tasklet_remove_from_tasklet_list(struct tasklet *t) -{ - LIST_DEL_INIT(&t->list); - _HA_ATOMIC_SUB(&tasks_run_queue, 1); -} - static inline void tasklet_remove_from_tasklet_list(struct tasklet *t) { - if (MT_LIST_DEL((struct mt_list *)&t->list)) + if (MT_LIST_DEL((struct mt_list *)&t->list)) { + _HA_ATOMIC_AND(&t->state, ~TASK_IN_LIST); _HA_ATOMIC_SUB(&tasks_run_queue, 1); + } } /* diff -Nru haproxy-2.2.8/include/haproxy/task-t.h haproxy-2.2.9/include/haproxy/task-t.h --- haproxy-2.2.8/include/haproxy/task-t.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/task-t.h 2021-02-06 16:02:45.000000000 +0000 @@ -39,6 +39,7 @@ * threads, must be set before first queue/wakeup */ #define TASK_SELF_WAKING 0x0010 /* task/tasklet found waking itself */ #define TASK_KILLED 0x0020 /* task/tasklet killed, may now be freed */ +#define TASK_IN_LIST 0x0040 /* tasklet is in a tasklet list */ #define TASK_WOKEN_INIT 0x0100 /* woken up for initialisation purposes */ #define TASK_WOKEN_TIMER 0x0200 /* woken up because of expired timer */ diff -Nru haproxy-2.2.8/include/haproxy/tools.h haproxy-2.2.9/include/haproxy/tools.h --- haproxy-2.2.8/include/haproxy/tools.h 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/include/haproxy/tools.h 2021-02-06 16:02:45.000000000 +0000 @@ -996,7 +996,7 @@ void dump_addr_and_bytes(struct buffer *buf, const char *pfx, const void *addr, int n); void dump_hex(struct buffer *out, const char *pfx, const void *buf, int len, int unsafe); int may_access(const void *ptr); -const void *resolve_sym_name(struct buffer *buf, const char *pfx, void *addr); +const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *addr); const char *get_exec_path(); #if defined(USE_BACKTRACE) diff -Nru haproxy-2.2.8/Makefile haproxy-2.2.9/Makefile --- haproxy-2.2.8/Makefile 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/Makefile 2021-02-06 16:02:45.000000000 +0000 @@ -834,8 +834,8 @@ DEP = $(INCLUDES) .build_opts help: - $(Q)sed -ne "/^[^#]*$$/q;s/^# \{0,1\}\(.*\)/\1/;p" Makefile - $(Q)echo; \ + @sed -ne "/^[^#]*$$/q;s/^# \{0,1\}\(.*\)/\1/;p" Makefile + @echo; \ if [ -n "$(TARGET)" ]; then \ if [ -n "$(set_target_defaults)" ]; then \ echo "Current TARGET: $(TARGET)"; \ @@ -847,10 +847,10 @@ echo " linux-glibc, linux-glibc-legacy, solaris, freebsd, netbsd, osx,"; \ echo " openbsd, aix51, aix52, aix72-gcc, cygwin, haiku, generic, custom"; \ fi - $(Q)echo;echo "Enabled features for TARGET '$(TARGET)' (disable with 'USE_xxx=') :" - $(Q)set -- $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt)),$(opt),)); echo " $$*" | (fmt || cat) 2>/dev/null - $(Q)echo;echo "Disabled features for TARGET '$(TARGET)' (enable with 'USE_xxx=1') :" - $(Q)set -- $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt)),,$(opt))); echo " $$*" | (fmt || cat) 2>/dev/null + @echo;echo "Enabled features for TARGET '$(TARGET)' (disable with 'USE_xxx=') :" + @set -- $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt)),$(opt),)); echo " $$*" | (fmt || cat) 2>/dev/null + @echo;echo "Disabled features for TARGET '$(TARGET)' (enable with 'USE_xxx=1') :" + @set -- $(foreach opt,$(patsubst USE_%,%,$(use_opts)),$(if $(USE_$(opt)),,$(opt))); echo " $$*" | (fmt || cat) 2>/dev/null # Used only to force a rebuild if some build options change, but we don't do # it for certain targets which take no build options @@ -982,7 +982,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 @@ -1012,7 +1011,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.2.8/reg-tests/ssl/set_ssl_cert.vtc haproxy-2.2.9/reg-tests/ssl/set_ssl_cert.vtc --- haproxy-2.2.8/reg-tests/ssl/set_ssl_cert.vtc 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/reg-tests/ssl/set_ssl_cert.vtc 2021-02-06 16:02:45.000000000 +0000 @@ -86,3 +86,14 @@ rxresp expect resp.status == 200 } -run + +shell { + printf "set ssl cert ${testdir}/common.pem <<\n$(cat ${testdir}/common.pem)\n\n" | socat "${tmpdir}/h1/stats" - + echo "abort ssl cert ${testdir}/common.pem" | socat "${tmpdir}/h1/stats" - +} + +haproxy h1 -cli { + send "show ssl cert ${testdir}/common.pem" + expect ~ ".*SHA1 FingerPrint: A490D069DBAFBEE66DE434BEC34030ADE8BCCBF1" +} + diff -Nru haproxy-2.2.8/scripts/run-regtests.sh haproxy-2.2.9/scripts/run-regtests.sh --- haproxy-2.2.8/scripts/run-regtests.sh 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/scripts/run-regtests.sh 2021-02-06 16:02:45.000000000 +0000 @@ -128,7 +128,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.2.8/src/cfgparse-global.c haproxy-2.2.9/src/cfgparse-global.c --- haproxy-2.2.8/src/cfgparse-global.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/cfgparse-global.c 2021-02-06 16:02:45.000000000 +0000 @@ -198,14 +198,7 @@ global.tune.maxaccept = max; } else if (!strcmp(args[0], "tune.chksize")) { - if (alertif_too_many_args(1, file, linenum, args, &err_code)) - goto out; - if (*(args[1]) == 0) { - ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n", file, linenum, args[0]); - err_code |= ERR_ALERT | ERR_FATAL; - goto out; - } - global.tune.chksize = atol(args[1]); + /* Deprecated now */ } else if (!strcmp(args[0], "tune.recv_enough")) { if (alertif_too_many_args(1, file, linenum, args, &err_code)) diff -Nru haproxy-2.2.8/src/cfgparse-listen.c haproxy-2.2.9/src/cfgparse-listen.c --- haproxy-2.2.8/src/cfgparse-listen.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/cfgparse-listen.c 2021-02-06 16:02:45.000000000 +0000 @@ -2932,6 +2932,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.2.8/src/check.c haproxy-2.2.9/src/check.c --- haproxy-2.2.8/src/check.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/check.c 2021-02-06 16:02:45.000000000 +0000 @@ -794,13 +794,17 @@ task_wakeup(check->task, TASK_WOKEN_IO); } - if (check->result != CHK_RES_UNKNOWN) { + if (check->result != CHK_RES_UNKNOWN || ret == -1) { /* Check complete or aborted. If connection not yet closed do it * now and wake the check task up to be sure the result is * handled ASAP. */ conn_sock_drain(conn); cs_close(cs); ret = -1; + + if (check->wait_list.events) + cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list); + /* We may have been scheduled to run, and the * I/O handler expects to have a cs, so remove * the tasklet @@ -883,8 +887,18 @@ if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) { chk_report_conn_err(check, 0, expired); } - else - goto out_unlock; /* timeout not reached, wait again */ + else { + if (check->state & CHK_ST_CLOSE_CONN) { + cs_destroy(cs); + cs = NULL; + conn = NULL; + check->cs = NULL; + check->state &= ~CHK_ST_CLOSE_CONN; + tcpcheck_main(check); + } + if (check->result == CHK_RES_UNKNOWN) + goto out_unlock; /* timeout not reached, wait again */ + } } /* check complete or aborted */ diff -Nru haproxy-2.2.8/src/cli.c haproxy-2.2.9/src/cli.c --- haproxy-2.2.8/src/cli.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/cli.c 2021-02-06 16:02:45.000000000 +0000 @@ -1025,13 +1025,17 @@ */ while (fd >= 0 && fd < global.maxsock) { struct fdtab fdt; - struct listener *li = NULL; - struct server *sv = NULL; - struct proxy *px = NULL; + const struct listener *li = NULL; + const struct server *sv = NULL; + const struct proxy *px = NULL; + const struct connection *conn = NULL; const struct mux_ops *mux = NULL; - void *ctx = NULL; + const struct xprt_ops *xprt = NULL; + const void *ctx = NULL; + const void *xprt_ctx = NULL; uint32_t conn_flags = 0; int is_back = 0; + int suspicious = 0; fdt = fdtab[fd]; @@ -1045,17 +1049,27 @@ goto skip; // closed } else if (fdt.iocb == conn_fd_handler) { - conn_flags = ((struct connection *)fdt.owner)->flags; - mux = ((struct connection *)fdt.owner)->mux; - ctx = ((struct connection *)fdt.owner)->ctx; - li = objt_listener(((struct connection *)fdt.owner)->target); - sv = objt_server(((struct connection *)fdt.owner)->target); - px = objt_proxy(((struct connection *)fdt.owner)->target); - is_back = conn_is_back((struct connection *)fdt.owner); + conn = (const struct connection *)fdt.owner; + conn_flags = conn->flags; + mux = conn->mux; + ctx = conn->ctx; + xprt = conn->xprt; + xprt_ctx = conn->xprt_ctx; + li = objt_listener(conn->target); + sv = objt_server(conn->target); + px = objt_proxy(conn->target); + is_back = conn_is_back(conn); + if (atleast2(fdt.thread_mask)) + suspicious = 1; + if (conn->handle.fd != fd) + suspicious = 1; } else if (fdt.iocb == listener_accept) li = fdt.owner; + if (!fdt.thread_mask) + suspicious = 1; + chunk_printf(&trash, " %5d : st=0x%02x(R:%c%c W:%c%c) ev=0x%02x(%c%c%c%c%c) [%c%c] tmask=0x%lx umask=0x%lx owner=%p iocb=%p(", fd, @@ -1082,6 +1096,33 @@ } else if (fdt.iocb == conn_fd_handler) { chunk_appendf(&trash, ") back=%d cflg=0x%08x", is_back, conn_flags); + + if (conn->handle.fd != fd) { + chunk_appendf(&trash, " fd=%d(BOGUS)", conn->handle.fd); + suspicious = 1; + } else { + struct sockaddr_storage sa; + socklen_t salen; + + salen = sizeof(sa); + if (getsockname(fd, (struct sockaddr *)&sa, &salen) != -1) { + if (sa.ss_family == AF_INET) + chunk_appendf(&trash, " fam=ipv4 lport=%d", ntohs(((const struct sockaddr_in *)&sa)->sin_port)); + else if (sa.ss_family == AF_INET6) + chunk_appendf(&trash, " fam=ipv6 lport=%d", ntohs(((const struct sockaddr_in6 *)&sa)->sin6_port)); + else if (sa.ss_family == AF_UNIX) + chunk_appendf(&trash, " fam=unix"); + } + + salen = sizeof(sa); + if (getpeername(fd, (struct sockaddr *)&sa, &salen) != -1) { + if (sa.ss_family == AF_INET) + chunk_appendf(&trash, " rport=%d", ntohs(((const struct sockaddr_in *)&sa)->sin_port)); + else if (sa.ss_family == AF_INET6) + chunk_appendf(&trash, " rport=%d", ntohs(((const struct sockaddr_in6 *)&sa)->sin6_port)); + } + } + if (px) chunk_appendf(&trash, " px=%s", px->id); else if (sv) @@ -1091,22 +1132,49 @@ if (mux) { chunk_appendf(&trash, " mux=%s ctx=%p", mux->name, ctx); + if (!ctx) + suspicious = 1; if (mux->show_fd) - mux->show_fd(&trash, fdt.owner); + suspicious |= mux->show_fd(&trash, fdt.owner); } else chunk_appendf(&trash, " nomux"); + + chunk_appendf(&trash, " xprt=%s", xprt ? xprt->name : ""); + if (xprt) { + if (xprt_ctx || xprt->show_fd) + chunk_appendf(&trash, " xprt_ctx=%p", xprt_ctx); + if (xprt->show_fd) + suspicious |= xprt->show_fd(&trash, conn, xprt_ctx); + } } else if (fdt.iocb == listener_accept) { + struct sockaddr_storage sa; + socklen_t salen; + chunk_appendf(&trash, ") l.st=%s fe=%s", listener_state_str(li), li->bind_conf->frontend->id); + + salen = sizeof(sa); + if (getsockname(fd, (struct sockaddr *)&sa, &salen) != -1) { + if (sa.ss_family == AF_INET) + chunk_appendf(&trash, " fam=ipv4 lport=%d", ntohs(((const struct sockaddr_in *)&sa)->sin_port)); + else if (sa.ss_family == AF_INET6) + chunk_appendf(&trash, " fam=ipv6 lport=%d", ntohs(((const struct sockaddr_in6 *)&sa)->sin6_port)); + else if (sa.ss_family == AF_UNIX) + chunk_appendf(&trash, " fam=unix"); + } } + else + chunk_appendf(&trash, ")"); #ifdef DEBUG_FD chunk_appendf(&trash, " evcnt=%u", fdtab[fd].event_count); + if (fdtab[fd].event_count >= 1000000) + suspicious = 1; #endif - chunk_appendf(&trash, ")\n"); + chunk_appendf(&trash, "%s\n", suspicious ? " !" : ""); if (ci_putchk(si_ic(si), &trash) == -1) { si_rx_room_blk(si); diff -Nru haproxy-2.2.8/src/dns.c haproxy-2.2.9/src/dns.c --- haproxy-2.2.8/src/dns.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/dns.c 2021-02-06 16:02:45.000000000 +0000 @@ -828,6 +828,9 @@ if (dns_answer_record == NULL) goto invalid_resp; + /* initialization */ + dns_answer_record->ar_item = NULL; + offset = 0; len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0); @@ -955,6 +958,10 @@ dns_answer_record->data_len = len; memcpy(dns_answer_record->target, tmpname, len); dns_answer_record->target[len] = 0; + if (dns_answer_record->ar_item != NULL) { + pool_free(dns_answer_item_pool, dns_answer_record->ar_item); + dns_answer_record->ar_item = NULL; + } break; case DNS_RTYPE_AAAA: @@ -1204,10 +1211,9 @@ // looking for the SRV record in the response list linked to this additional record list_for_each_entry(tmp_record, &dns_p->answer_list, list) { if (tmp_record->type == DNS_RTYPE_SRV && + tmp_record->ar_item == NULL && !dns_hostname_cmp(tmp_record->target, dns_answer_record->name, tmp_record->data_len)) { /* Always use the received additional record to refresh info */ - if (tmp_record->ar_item) - pool_free(dns_answer_item_pool, tmp_record->ar_item); tmp_record->ar_item = dns_answer_record; break; } diff -Nru haproxy-2.2.8/src/filters.c haproxy-2.2.9/src/filters.c --- haproxy-2.2.8/src/filters.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/filters.c 2021-02-06 16:02:45.000000000 +0000 @@ -632,6 +632,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) { @@ -656,11 +658,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.2.8/src/h1_htx.c haproxy-2.2.9/src/h1_htx.c --- haproxy-2.2.8/src/h1_htx.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/h1_htx.c 2021-02-06 16:02:45.000000000 +0000 @@ -485,7 +485,7 @@ total += ret; } if (h1m->state == H1_MSG_CHUNK_SIZE) { - unsigned int chksz; + uint64_t chksz; ret = h1_parse_chunk_size(srcbuf, ofs, b_data(srcbuf), &chksz); if (ret <= 0) diff -Nru haproxy-2.2.8/src/haproxy.c haproxy-2.2.9/src/haproxy.c --- haproxy-2.2.8/src/haproxy.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/haproxy.c 2021-02-06 16:02:45.000000000 +0000 @@ -161,7 +161,6 @@ .options = GTUNE_LISTENER_MQ, .bufsize = (BUFSIZE + 2*sizeof(void *) - 1) & -(2*sizeof(void *)), .maxrewrite = MAXREWRITE, - .chksize = (BUFSIZE + 2*sizeof(void *) - 1) & -(2*sizeof(void *)), .reserved_bufs = RESERVED_BUFS, .pattern_cache = DEFAULT_PAT_LRU_SIZE, .pool_low_ratio = 20, @@ -2028,7 +2027,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(); @@ -2040,22 +2040,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 @@ -2064,10 +2069,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) { @@ -2639,6 +2649,9 @@ 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) diff -Nru haproxy-2.2.8/src/hlua.c haproxy-2.2.9/src/hlua.c --- haproxy-2.2.8/src/hlua.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/hlua.c 2021-02-06 16:02:45.000000000 +0000 @@ -10,6 +10,8 @@ * */ +#define _GNU_SOURCE + #include #include diff -Nru haproxy-2.2.8/src/hlua_fcn.c haproxy-2.2.9/src/hlua_fcn.c --- haproxy-2.2.8/src/hlua_fcn.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/hlua_fcn.c 2021-02-06 16:02:45.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 diff -Nru haproxy-2.2.8/src/mux_fcgi.c haproxy-2.2.9/src/mux_fcgi.c --- haproxy-2.2.8/src/mux_fcgi.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/mux_fcgi.c 2021-02-06 16:02:45.000000000 +0000 @@ -353,7 +353,8 @@ static struct task *fcgi_timeout_task(struct task *t, void *context, unsigned short state); static int fcgi_process(struct fcgi_conn *fconn); -static struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned short state); +/* fcgi_io_cb is exported to see it resolved in "show fd" */ +struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned short state); static inline struct fcgi_strm *fcgi_conn_st_by_id(struct fcgi_conn *fconn, int id); static struct task *fcgi_deferred_shut(struct task *t, void *ctx, unsigned short state); static struct fcgi_strm *fcgi_conn_stream_new(struct fcgi_conn *fconn, struct conn_stream *cs, struct session *sess); @@ -2926,7 +2927,7 @@ } /* this is the tasklet referenced in fconn->wait_event.tasklet */ -static struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned short status) +struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned short status) { struct connection *conn; struct fcgi_conn *fconn; @@ -4050,7 +4051,7 @@ } /* for debugging with CLI's "show fd" command */ -static void fcgi_show_fd(struct buffer *msg, struct connection *conn) +static int fcgi_show_fd(struct buffer *msg, struct connection *conn) { struct fcgi_conn *fconn = conn->ctx; struct fcgi_strm *fstrm = NULL; @@ -4061,7 +4062,7 @@ struct buffer *hmbuf, *tmbuf; if (!fconn) - return; + return 0; list_for_each_entry(fstrm, &fconn->send_list, send_list) send_cnt++; @@ -4101,7 +4102,19 @@ if (fstrm->cs) chunk_appendf(msg, " .cs.flg=0x%08x .cs.data=%p", fstrm->cs->flags, fstrm->cs->data); + chunk_appendf(&trash, " .subs=%p", fstrm->subs); + if (fstrm->subs) { + if (fstrm->subs) { + chunk_appendf(&trash, "(ev=%d tl=%p", fstrm->subs->events, fstrm->subs->tasklet); + chunk_appendf(&trash, " tl.calls=%d tl.ctx=%p tl.fct=", + fstrm->subs->tasklet->calls, + fstrm->subs->tasklet->context); + resolve_sym_name(&trash, NULL, fstrm->subs->tasklet->process); + chunk_appendf(&trash, ")"); + } + } } + return 0; } /* Migrate the the connection to the current thread. diff -Nru haproxy-2.2.8/src/mux_h1.c haproxy-2.2.9/src/mux_h1.c --- haproxy-2.2.8/src/mux_h1.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/mux_h1.c 2021-02-06 16:02:45.000000000 +0000 @@ -2260,7 +2260,7 @@ return -1; } -static struct task *h1_io_cb(struct task *t, void *ctx, unsigned short status) +struct task *h1_io_cb(struct task *t, void *ctx, unsigned short status) { struct connection *conn; struct tasklet *tl = (struct tasklet *)t; @@ -2895,10 +2895,11 @@ } /* for debugging with CLI's "show fd" command */ -static void h1_show_fd(struct buffer *msg, struct connection *conn) +static int h1_show_fd(struct buffer *msg, struct connection *conn) { struct h1c *h1c = conn->ctx; struct h1s *h1s = h1c->h1s; + int ret = 0; chunk_appendf(msg, " h1c.flg=0x%x .sub=%d .ibuf=%u@%p+%u/%u .obuf=%u@%p+%u/%u", h1c->flags, h1c->wait_event.events, @@ -2922,7 +2923,22 @@ if (h1s->cs) chunk_appendf(msg, " .cs.flg=0x%08x .cs.data=%p", h1s->cs->flags, h1s->cs->data); + + chunk_appendf(&trash, " .subs=%p", h1s->subs); + if (h1s->subs) { + if (h1s->subs) { + chunk_appendf(&trash, "(ev=%d tl=%p", h1s->subs->events, h1s->subs->tasklet); + chunk_appendf(&trash, " tl.calls=%d tl.ctx=%p tl.fct=", + h1s->subs->tasklet->calls, + h1s->subs->tasklet->context); + if (h1s->subs->tasklet->calls >= 1000000) + ret = 1; + resolve_sym_name(&trash, NULL, h1s->subs->tasklet->process); + chunk_appendf(&trash, ")"); + } + } } + return ret; } diff -Nru haproxy-2.2.8/src/mux_h2.c haproxy-2.2.9/src/mux_h2.c --- haproxy-2.2.8/src/mux_h2.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/mux_h2.c 2021-02-06 16:02:45.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 { @@ -439,7 +441,8 @@ static int h2_send(struct h2c *h2c); static int h2_recv(struct h2c *h2c); static int h2_process(struct h2c *h2c); -static struct task *h2_io_cb(struct task *t, void *ctx, unsigned short state); +/* h2_io_cb is exported to see it resolved in "show fd" */ +struct task *h2_io_cb(struct task *t, void *ctx, unsigned short state); static inline struct h2s *h2c_st_by_id(struct h2c *h2c, int id); static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *flags, unsigned long long *body_len); static int h2_frt_transfer_data(struct h2s *h2s); @@ -550,15 +553,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 @@ -2952,7 +2955,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) { @@ -3183,9 +3186,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; } @@ -3209,8 +3215,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) || @@ -3231,6 +3236,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 */ @@ -3365,6 +3379,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 @@ -3383,9 +3402,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); @@ -3517,7 +3541,7 @@ } /* this is the tasklet referenced in h2c->wait_event.tasklet */ -static struct task *h2_io_cb(struct task *t, void *ctx, unsigned short status) +struct task *h2_io_cb(struct task *t, void *ctx, unsigned short status) { struct connection *conn; struct tasklet *tl = (struct tasklet *)t; @@ -3587,7 +3611,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) @@ -6006,7 +6031,7 @@ } /* for debugging with CLI's "show fd" command */ -static void h2_show_fd(struct buffer *msg, struct connection *conn) +static int h2_show_fd(struct buffer *msg, struct connection *conn) { struct h2c *h2c = conn->ctx; struct h2s *h2s = NULL; @@ -6016,9 +6041,10 @@ int tree_cnt = 0; int orph_cnt = 0; struct buffer *hmbuf, *tmbuf; + int ret = 0; if (!h2c) - return; + return ret; list_for_each_entry(h2s, &h2c->fctl_list, list) fctl_cnt++; @@ -6055,15 +6081,30 @@ (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), h2s->cs); if (h2s->cs) - chunk_appendf(msg, " .cs.flg=0x%08x .cs.data=%p", + chunk_appendf(msg, "(.flg=0x%08x .data=%p)", h2s->cs->flags, h2s->cs->data); + + chunk_appendf(&trash, " .subs=%p", h2s->subs); + if (h2s->subs) { + if (h2s->subs) { + chunk_appendf(&trash, "(ev=%d tl=%p", h2s->subs->events, h2s->subs->tasklet); + chunk_appendf(&trash, " tl.calls=%d tl.ctx=%p tl.fct=", + h2s->subs->tasklet->calls, + h2s->subs->tasklet->context); + if (h2s->subs->tasklet->calls >= 1000000) + ret = 1; + resolve_sym_name(&trash, NULL, h2s->subs->tasklet->process); + chunk_appendf(&trash, ")"); + } + } } + return ret; } /* Migrate the the connection to the current thread. diff -Nru haproxy-2.2.8/src/mux_pt.c haproxy-2.2.9/src/mux_pt.c --- haproxy-2.2.8/src/mux_pt.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/mux_pt.c 2021-02-06 16:02:45.000000000 +0000 @@ -53,8 +53,10 @@ } } -/* Callback, used when we get I/Os while in idle mode */ -static struct task *mux_pt_io_cb(struct task *t, void *tctx, unsigned short status) +/* Callback, used when we get I/Os while in idle mode. This one is exported so + * that "show fd" can resolve it. + */ +struct task *mux_pt_io_cb(struct task *t, void *tctx, unsigned short status) { struct mux_pt_ctx *ctx = tctx; diff -Nru haproxy-2.2.8/src/mworker.c haproxy-2.2.9/src/mworker.c --- haproxy-2.2.8/src/mworker.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/mworker.c 2021-02-06 16:02:45.000000000 +0000 @@ -10,6 +10,8 @@ * */ +#define _GNU_SOURCE + #include #include #include diff -Nru haproxy-2.2.8/src/payload.c haproxy-2.2.9/src/payload.c --- haproxy-2.2.8/src/payload.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/payload.c 2021-02-06 16:02:45.000000000 +0000 @@ -976,14 +976,13 @@ chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req; head = ci_head(chn); data = ci_data(chn); - max = global.tune.bufsize; } else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) { struct buffer *buf = &(__objt_check(smp->sess->origin)->bi); head = b_head(buf); data = b_data(buf); - max = global.tune.chksize; } + max = global.tune.bufsize; if (!head) return 0; @@ -1039,14 +1038,13 @@ chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req; head = ci_head(chn); data = ci_data(chn); - max = global.tune.bufsize; } else if (obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) { struct buffer *buf = &(__objt_check(smp->sess->origin)->bi); head = b_head(buf); data = b_data(buf); - max = global.tune.chksize; } + max = global.tune.bufsize; if (!head) return 0; diff -Nru haproxy-2.2.8/src/peers.c haproxy-2.2.9/src/peers.c --- haproxy-2.2.8/src/peers.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/peers.c 2021-02-06 16:02:45.000000000 +0000 @@ -298,6 +298,18 @@ static const struct trace_event peers_trace_events[] = { #define PEERS_EV_UPDTMSG (1 << 0) { .mask = PEERS_EV_UPDTMSG, .name = "updtmsg", .desc = "update message received" }, +#define PEERS_EV_ACKMSG (1 << 1) + { .mask = PEERS_EV_ACKMSG, .name = "ackmsg", .desc = "ack message received" }, +#define PEERS_EV_SWTCMSG (1 << 2) + { .mask = PEERS_EV_SWTCMSG, .name = "swtcmsg", .desc = "switch message received" }, +#define PEERS_EV_DEFMSG (1 << 3) + { .mask = PEERS_EV_DEFMSG, .name = "defmsg", .desc = "definition message received" }, +#define PEERS_EV_CTRLMSG (1 << 4) + { .mask = PEERS_EV_CTRLMSG, .name = "ctrlmsg", .desc = "control message sent/received" }, +#define PEERS_EV_SESSREL (1 << 5) + { .mask = PEERS_EV_SESSREL, .name = "sessrl", .desc = "peer session releasing" }, +#define PEERS_EV_PROTOERR (1 << 6) + { .mask = PEERS_EV_PROTOERR, .name = "protoerr", .desc = "protocol error" }, }; static const struct name_desc peers_trace_lockon_args[4] = { @@ -325,6 +337,25 @@ .report_events = ~0, /* report everything by default */ }; +/* Return peer control message types as strings (only for debugging purpose). */ +static inline char *ctrl_msg_type_str(unsigned int type) +{ + switch (type) { + case PEER_MSG_CTRL_RESYNCREQ: + return "RESYNCREQ"; + case PEER_MSG_CTRL_RESYNCFINISHED: + return "RESYNCFINISHED"; + case PEER_MSG_CTRL_RESYNCPARTIAL: + return "RESYNCPARTIAL"; + case PEER_MSG_CTRL_RESYNCCONFIRM: + return "RESYNCCONFIRM"; + case PEER_MSG_CTRL_HEARTBEAT: + return "HEARTBEAT"; + default: + return "???"; + } +} + #define TRACE_SOURCE &trace_peers INITCALL1(STG_REGISTER, trace_register_source, TRACE_SOURCE); @@ -333,7 +364,7 @@ const struct ist where, const struct ist func, const void *a1, const void *a2, const void *a3, const void *a4) { - if (mask & PEERS_EV_UPDTMSG) { + if (mask & (PEERS_EV_UPDTMSG|PEERS_EV_ACKMSG|PEERS_EV_SWTCMSG)) { if (a2) { const struct peer *peer = a2; @@ -350,6 +381,73 @@ chunk_appendf(&trace_buf, " %llu", (unsigned long long)*val); } } + + if (mask & PEERS_EV_DEFMSG) { + if (a2) { + const struct peer *peer = a2; + + chunk_appendf(&trace_buf, " peer=%s", peer->id); + } + if (a3) { + const char *p = a3; + + chunk_appendf(&trace_buf, " @%p", p); + } + if (a4) { + const int *val = a4; + + chunk_appendf(&trace_buf, " %d", *val); + } + } + + if (mask & PEERS_EV_CTRLMSG) { + if (a2) { + const unsigned char *ctrl_msg_type = a2; + + chunk_appendf(&trace_buf, " %s", ctrl_msg_type_str(*ctrl_msg_type)); + + } + if (a3) { + const char *local_peer = a3; + + chunk_appendf(&trace_buf, " %s", local_peer); + } + + if (a4) { + const char *remote_peer = a4; + + chunk_appendf(&trace_buf, " -> %s", remote_peer); + } + } + + if (mask & (PEERS_EV_SESSREL|PEERS_EV_PROTOERR)) { + if (a2) { + const struct peer *peer = a2; + struct peers *peers = NULL; + + if (peer && peer->appctx) { + struct stream_interface *si; + + si = peer->appctx->owner; + if (si) { + struct stream *s = si_strm(si); + + peers = strm_fe(s)->parent; + } + } + + if (peers) + chunk_appendf(&trace_buf, " %s", peers->local->id); + if (peer) + chunk_appendf(&trace_buf, " -> %s", peer->id); + } + + if (a3) { + const int *prev_state = a3; + + chunk_appendf(&trace_buf, " prev_state=%d\n", *prev_state); + } + } } static const char *statuscode_str(int statuscode) @@ -863,6 +961,7 @@ { struct peer *peer = appctx->ctx.peers.ptr; + TRACE_PROTO("releasing peer session", PEERS_EV_SESSREL, NULL, peer); /* appctx->ctx.peers.ptr is not a peer session */ if (appctx->st0 < PEER_SESS_ST_SENDSUCCESS) return; @@ -1094,12 +1193,16 @@ * any other negative returned value must be considered as an error with an appctx st0 * returned value equal to PEER_SESS_ST_END. */ -static inline int peer_send_resync_reqmsg(struct appctx *appctx) +static inline int peer_send_resync_reqmsg(struct appctx *appctx, + struct peer *peer, struct peers *peers) { struct peer_prep_params p = { .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCREQ, }, }; + TRACE_PROTO("send control message", PEERS_EV_CTRLMSG, + NULL, &p.control.head[1], peers->local->id, peer->id); + return peer_send_msg(appctx, peer_prepare_control_msg, &p); } @@ -1110,12 +1213,16 @@ * any other negative returned value must be considered as an error with an appctx st0 * returned value equal to PEER_SESS_ST_END. */ -static inline int peer_send_resync_confirmsg(struct appctx *appctx) +static inline int peer_send_resync_confirmsg(struct appctx *appctx, + struct peer *peer, struct peers *peers) { struct peer_prep_params p = { .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_RESYNCCONFIRM, }, }; + TRACE_PROTO("send control message", PEERS_EV_CTRLMSG, + NULL, &p.control.head[1], peers->local->id, peer->id); + return peer_send_msg(appctx, peer_prepare_control_msg, &p); } @@ -1126,7 +1233,8 @@ * any other negative returned value must be considered as an error with an appctx st0 * returned value equal to PEER_SESS_ST_END. */ -static inline int peer_send_resync_finishedmsg(struct appctx *appctx, struct peers *peers) +static inline int peer_send_resync_finishedmsg(struct appctx *appctx, + struct peer *peer, struct peers *peers) { struct peer_prep_params p = { .control.head = { PEER_MSG_CLASS_CONTROL, }, @@ -1135,6 +1243,9 @@ p.control.head[1] = (peers->flags & PEERS_RESYNC_STATEMASK) == PEERS_RESYNC_FINISHED ? PEER_MSG_CTRL_RESYNCFINISHED : PEER_MSG_CTRL_RESYNCPARTIAL; + TRACE_PROTO("send control message", PEERS_EV_CTRLMSG, + NULL, &p.control.head[1], peers->local->id, peer->id); + return peer_send_msg(appctx, peer_prepare_control_msg, &p); } @@ -1145,12 +1256,16 @@ * any other negative returned value must be considered as an error with an appctx st0 * returned value equal to PEER_SESS_ST_END. */ -static inline int peer_send_heartbeatmsg(struct appctx *appctx) +static inline int peer_send_heartbeatmsg(struct appctx *appctx, + struct peer *peer, struct peers *peers) { struct peer_prep_params p = { .control.head = { PEER_MSG_CLASS_CONTROL, PEER_MSG_CTRL_HEARTBEAT, }, }; + TRACE_PROTO("send control message", PEERS_EV_CTRLMSG, + NULL, &p.control.head[1], peers->local->id, peer->id); + return peer_send_msg(appctx, peer_prepare_control_msg, &p); } @@ -1695,6 +1810,9 @@ table_id = intdecode(msg_cur, msg_end); if (!*msg_cur || (*msg_cur + sizeof(update) > msg_end)) { /* malformed message */ + + TRACE_PROTO("malformed message", PEERS_EV_ACKMSG, + NULL, p, *msg_cur); appctx->st0 = PEER_SESS_ST_ERRPROTO; return 0; } @@ -1728,6 +1846,7 @@ table_id = intdecode(msg_cur, msg_end); if (!*msg_cur) { + TRACE_PROTO("malformed message", PEERS_EV_SWTCMSG, NULL, p); /* malformed message */ appctx->st0 = PEER_SESS_ST_ERRPROTO; return 0; @@ -1765,16 +1884,22 @@ uint64_t table_data; table_id = intdecode(msg_cur, msg_end); - if (!*msg_cur) + if (!*msg_cur) { + TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p); goto malformed_exit; + } table_id_len = intdecode(msg_cur, msg_end); - if (!*msg_cur) + if (!*msg_cur) { + TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p, *msg_cur); goto malformed_exit; + } p->remote_table = NULL; - if (!table_id_len || (*msg_cur + table_id_len) >= msg_end) + if (!table_id_len || (*msg_cur + table_id_len) >= msg_end) { + TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p, *msg_cur, &table_id_len); goto malformed_exit; + } for (st = p->tables; st; st = st->next) { /* Reset IDs */ @@ -1786,28 +1911,39 @@ p->remote_table = st; } - if (!p->remote_table) + if (!p->remote_table) { + TRACE_PROTO("ignored message", PEERS_EV_DEFMSG, NULL, p); goto ignore_msg; + } *msg_cur += table_id_len; - if (*msg_cur >= msg_end) + if (*msg_cur >= msg_end) { + TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p); goto malformed_exit; + } table_type = intdecode(msg_cur, msg_end); - if (!*msg_cur) + if (!*msg_cur) { + TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p); goto malformed_exit; + } table_keylen = intdecode(msg_cur, msg_end); - if (!*msg_cur) + if (!*msg_cur) { + TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p); goto malformed_exit; + } table_data = intdecode(msg_cur, msg_end); - if (!*msg_cur) + if (!*msg_cur) { + TRACE_PROTO("malformed message", PEERS_EV_DEFMSG, NULL, p); goto malformed_exit; + } if (p->remote_table->table->type != peer_int_key_type[table_type] || p->remote_table->table->key_size != table_keylen) { p->remote_table = NULL; + TRACE_PROTO("ignored message", PEERS_EV_DEFMSG, NULL, p); goto ignore_msg; } @@ -1922,6 +2058,8 @@ struct shared_table *st; /* Reset message: remote need resync */ + TRACE_PROTO("received control message", PEERS_EV_CTRLMSG, + NULL, &msg_head[1], peers->local->id, peer->id); /* prepare tables for a global push */ for (st = peer->tables; st; st = st->next) { st->teaching_origin = st->last_pushed = st->table->update; @@ -1935,6 +2073,8 @@ peer->flags |= PEER_F_TEACH_PROCESS; } else if (msg_head[1] == PEER_MSG_CTRL_RESYNCFINISHED) { + TRACE_PROTO("received control message", PEERS_EV_CTRLMSG, + NULL, &msg_head[1], peers->local->id, peer->id); if (peer->flags & PEER_F_LEARN_ASSIGN) { peer->flags &= ~PEER_F_LEARN_ASSIGN; peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS); @@ -1943,6 +2083,8 @@ peer->confirm++; } else if (msg_head[1] == PEER_MSG_CTRL_RESYNCPARTIAL) { + TRACE_PROTO("received control message", PEERS_EV_CTRLMSG, + NULL, &msg_head[1], peers->local->id, peer->id); if (peer->flags & PEER_F_LEARN_ASSIGN) { peer->flags &= ~PEER_F_LEARN_ASSIGN; peers->flags &= ~(PEERS_F_RESYNC_ASSIGN|PEERS_F_RESYNC_PROCESS); @@ -1956,6 +2098,8 @@ else if (msg_head[1] == PEER_MSG_CTRL_RESYNCCONFIRM) { struct shared_table *st; + TRACE_PROTO("received control message", PEERS_EV_CTRLMSG, + NULL, &msg_head[1], peers->local->id, peer->id); /* If stopping state */ if (stopping) { /* Close session, push resync no more needed */ @@ -1972,6 +2116,8 @@ peer->flags &= PEER_TEACH_RESET; } else if (msg_head[1] == PEER_MSG_CTRL_HEARTBEAT) { + TRACE_PROTO("received control message", PEERS_EV_CTRLMSG, + NULL, &msg_head[1], peers->local->id, peer->id); peer->reconnect = tick_add(now_ms, MS_TO_TICKS(PEER_RECONNECT_TIMEOUT)); peer->rx_hbt++; } @@ -2018,19 +2164,17 @@ * -1 means an internal error occurred, 0 is for a peer protocol error leading * to a peer state change (from the peer I/O handler point of view). */ -static inline int peer_send_msgs(struct appctx *appctx, struct peer *peer) +static inline int peer_send_msgs(struct appctx *appctx, + struct peer *peer, struct peers *peers) { int repl; - struct stream_interface *si = appctx->owner; - struct stream *s = si_strm(si); - struct peers *peers = strm_fe(s)->parent; /* Need to request a resync */ if ((peer->flags & PEER_F_LEARN_ASSIGN) && (peers->flags & PEERS_F_RESYNC_ASSIGN) && !(peers->flags & PEERS_F_RESYNC_PROCESS)) { - repl = peer_send_resync_reqmsg(appctx); + repl = peer_send_resync_reqmsg(appctx, peer, peers); if (repl <= 0) return repl; @@ -2094,7 +2238,7 @@ } if ((peer->flags & PEER_F_TEACH_PROCESS) && !(peer->flags & PEER_F_TEACH_FINISHED)) { - repl = peer_send_resync_finishedmsg(appctx, peers); + repl = peer_send_resync_finishedmsg(appctx, peer, peers); if (repl <= 0) return repl; @@ -2104,7 +2248,7 @@ /* Confirm finished or partial messages */ while (peer->confirm) { - repl = peer_send_resync_confirmsg(appctx); + repl = peer_send_resync_confirmsg(appctx, peer, peers); if (repl <= 0) return repl; @@ -2524,7 +2668,7 @@ send_msgs: if (curpeer->flags & PEER_F_HEARTBEAT) { curpeer->flags &= ~PEER_F_HEARTBEAT; - repl = peer_send_heartbeatmsg(appctx); + repl = peer_send_heartbeatmsg(appctx, curpeer, curpeers); if (repl <= 0) { if (repl == -1) goto out; @@ -2533,7 +2677,7 @@ curpeer->tx_hbt++; } /* we get here when a peer_recv_msg() returns 0 in reql */ - repl = peer_send_msgs(appctx, curpeer); + repl = peer_send_msgs(appctx, curpeer, curpeers); if (repl <= 0) { if (repl == -1) goto out; @@ -2561,13 +2705,17 @@ goto switchstate; } case PEER_SESS_ST_ERRPROTO: { + TRACE_PROTO("protocol error", PEERS_EV_PROTOERR, + NULL, curpeer, &prev_state); if (curpeer) curpeer->proto_err++; if (prev_state == PEER_SESS_ST_WAITMSG) _HA_ATOMIC_SUB(&connected_peers, 1); prev_state = appctx->st0; - if (peer_send_error_protomsg(appctx) == -1) + if (peer_send_error_protomsg(appctx) == -1) { + TRACE_PROTO("could not send error message", PEERS_EV_PROTOERR); goto out; + } appctx->st0 = PEER_SESS_ST_END; prev_state = appctx->st0; } @@ -2652,6 +2800,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; @@ -2776,7 +2925,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.2.8/src/server.c haproxy-2.2.9/src/server.c --- haproxy-2.2.8/src/server.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/server.c 2021-02-06 16:02:45.000000000 +0000 @@ -1965,8 +1965,10 @@ err_code |= ERR_ALERT | ERR_FATAL; goto out; } - else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) - err_code |= ERR_WARN; + else if (failifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) { + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } /* There is no mandatory first arguments for default server. */ if (srv && parse_addr) { diff -Nru haproxy-2.2.8/src/session.c haproxy-2.2.9/src/session.c --- haproxy-2.2.8/src/session.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/session.c 2021-02-06 16:02:45.000000000 +0000 @@ -99,6 +99,7 @@ void conn_session_free(struct connection *conn) { session_free(conn->owner); + conn->owner = NULL; } /* count a new session to keep frontend, listener and track stats up to date */ diff -Nru haproxy-2.2.8/src/ssl_ckch.c haproxy-2.2.9/src/ssl_ckch.c --- haproxy-2.2.8/src/ssl_ckch.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/ssl_ckch.c 2021-02-06 16:02:45.000000000 +0000 @@ -101,9 +101,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); @@ -1807,7 +1805,6 @@ /* Only free the ckchs there, because the SNI and instances were not generated yet */ ckch_store_free(ckchs_transaction.new_ckchs); ckchs_transaction.new_ckchs = NULL; - ckch_store_free(ckchs_transaction.old_ckchs); ckchs_transaction.old_ckchs = NULL; free(ckchs_transaction.path); ckchs_transaction.path = NULL; diff -Nru haproxy-2.2.8/src/ssl_sock.c haproxy-2.2.9/src/ssl_sock.c --- haproxy-2.2.8/src/ssl_sock.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/ssl_sock.c 2021-02-06 16:02:45.000000000 +0000 @@ -139,6 +139,10 @@ DECLARE_STATIC_POOL(ssl_sock_ctx_pool, "ssl_sock_ctx_pool", sizeof(struct ssl_sock_ctx)); static struct task *ssl_sock_io_cb(struct task *, void *, unsigned short); + +/* ssl_sock_io_cb is exported to see it resolved in "show fd" */ +struct task *ssl_sock_io_cb(struct task *, void *, unsigned short); + static int ssl_sock_handshake(struct connection *conn, unsigned int flag); /* Methods to implement OpenSSL BIO */ @@ -5292,6 +5296,9 @@ { struct ssl_sock_ctx *ctx = conn->xprt_ctx; int ret; + socklen_t lskerr; + int skerr; + if (!conn_ctrl_ready(conn)) return 0; @@ -5299,6 +5306,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 @@ -5675,7 +5697,7 @@ return (ctx->xprt->remove_xprt(conn, ctx->xprt_ctx, toremove_ctx, newops, newctx)); } -static struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned short state) +struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned short state) { struct tasklet *tl = (struct tasklet *)t; struct ssl_sock_ctx *ctx = context; @@ -6447,6 +6469,56 @@ } #endif +/* "show fd" helper to dump ssl internals. Warning: the output buffer is often + * the common trash! It returns non-zero if the connection entry looks suspicious. + */ +static int ssl_sock_show_fd(struct buffer *buf, const struct connection *conn, const void *ctx) +{ + const struct ssl_sock_ctx *sctx = ctx; + int ret = 0; + + if (!sctx) + return ret; + + if (sctx->conn != conn) { + chunk_appendf(&trash, " xctx.conn=%p(BOGUS)", sctx->conn); + ret = 1; + } + chunk_appendf(&trash, " xctx.st=%d", sctx->xprt_st); + + if (sctx->xprt) { + chunk_appendf(&trash, " .xprt=%s", sctx->xprt->name); + if (sctx->xprt_ctx) + chunk_appendf(&trash, " .xctx=%p", sctx->xprt_ctx); + } + + chunk_appendf(&trash, " .wait.ev=%d", sctx->wait_event.events); + + /* as soon as a shutdown is reported the lower layer unregisters its + * subscriber, so the situations below are transient and rare enough to + * be reported as suspicious. In any case they shouldn't last. + */ + if ((sctx->wait_event.events & 1) && (conn->flags & (CO_FL_SOCK_RD_SH|CO_FL_ERROR))) + ret = 1; + if ((sctx->wait_event.events & 2) && (conn->flags & (CO_FL_SOCK_WR_SH|CO_FL_ERROR))) + ret = 1; + + chunk_appendf(&trash, " .subs=%p", sctx->subs); + if (sctx->subs) { + chunk_appendf(&trash, "(ev=%d tl=%p", sctx->subs->events, sctx->subs->tasklet); + if (sctx->subs->tasklet->calls >= 1000000) + ret = 1; + chunk_appendf(&trash, " tl.calls=%d tl.ctx=%p tl.fct=", + sctx->subs->tasklet->calls, + sctx->subs->tasklet->context); + resolve_sym_name(&trash, NULL, sctx->subs->tasklet->process); + chunk_appendf(&trash, ")"); + } + chunk_appendf(&trash, " .sent_early=%d", sctx->sent_early_data); + chunk_appendf(&trash, " .early_in=%d", (int)sctx->early_buf.data); + return ret; +} + /* This function is used with TLS ticket keys management. It permits to browse * each reference. The variable must contain the current node, * point to the root node. @@ -6737,6 +6809,7 @@ .get_alpn = ssl_sock_get_alpn, .takeover = ssl_takeover, .name = "SSL", + .show_fd = ssl_sock_show_fd, }; enum act_return ssl_action_wait_for_hs(struct act_rule *rule, struct proxy *px, diff -Nru haproxy-2.2.8/src/stats.c haproxy-2.2.9/src/stats.c --- haproxy-2.2.8/src/stats.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/stats.c 2021-02-06 16:02:45.000000000 +0000 @@ -146,6 +146,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] = { @@ -3448,6 +3449,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.2.8/src/stick_table.c haproxy-2.2.9/src/stick_table.c --- haproxy-2.2.8/src/stick_table.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/stick_table.c 2021-02-06 16:02:45.000000000 +0000 @@ -2217,7 +2217,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. */ @@ -2282,7 +2282,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. */ @@ -2814,7 +2814,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.2.8/src/task.c haproxy-2.2.9/src/task.c --- haproxy-2.2.8/src/task.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/task.c 2021-02-06 16:02:45.000000000 +0000 @@ -444,10 +444,13 @@ t->calls++; sched->current = t; + _HA_ATOMIC_SUB(&tasks_run_queue, 1); + if (TASK_IS_TASKLET(t)) { + LIST_DEL_INIT(&((struct tasklet *)t)->list); + __ha_barrier_store(); state = _HA_ATOMIC_XCHG(&t->state, state); __ha_barrier_atomic_store(); - __tasklet_remove_from_tasklet_list((struct tasklet *)t); process(t, ctx, state); done++; sched->current = NULL; @@ -455,9 +458,10 @@ continue; } + LIST_DEL_INIT(&((struct tasklet *)t)->list); + __ha_barrier_store(); state = _HA_ATOMIC_XCHG(&t->state, state | TASK_RUNNING); __ha_barrier_atomic_store(); - __tasklet_remove_from_tasklet_list((struct tasklet *)t); /* OK then this is a regular task */ diff -Nru haproxy-2.2.8/src/tcpcheck.c haproxy-2.2.9/src/tcpcheck.c --- haproxy-2.2.8/src/tcpcheck.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/tcpcheck.c 2021-02-06 16:02:45.000000000 +0000 @@ -984,20 +984,20 @@ struct tcpcheck_rule *next; int status, port; - /* For a connect action we'll create a new connection. We may also have - * to kill a previous one. But we don't want to leave *without* a + /* For a connect action we'll create a new connection. The previous one, + * if any should have been killed. We don't want to leave *without* a * connection if we came here from the connection layer, hence with a * connection. Thus we'll proceed in the following order : * 1: close but not release previous connection (handled by the caller) - * 2: try to get a new connection - * 3: release and replace the old one on success + * 2: Wait the connection is released (handled by process_chk_conn()) + * 3: try to get a new connection */ /* Always release input and output buffer when a new connect is evaluated */ check_release_buf(check, &check->bi); check_release_buf(check, &check->bo); - /* 2- prepare new connection */ + /* prepare new connection */ cs = cs_new(NULL); if (!cs) { chunk_printf(&trash, "TCPCHK error allocating connection at step %d", @@ -1009,19 +1009,6 @@ goto out; } - /* 3- release and replace the old one on success */ - if (check->cs) { - if (check->wait_list.events) - check->cs->conn->mux->unsubscribe(check->cs, check->wait_list.events, - &check->wait_list); - - /* We may have been scheduled to run, and the I/O handler - * expects to have a cs, so remove the tasklet - */ - tasklet_remove_from_tasklet_list(check->wait_list.tasklet); - cs_destroy(check->cs); - } - tasklet_set_tid(check->wait_list.tasklet, tid); check->cs = cs; @@ -1985,30 +1972,38 @@ /* 2- check if we are waiting for the connection establishment. It only * happens during TCPCHK_ACT_CONNECT. */ if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) { - if (conn->flags & CO_FL_WAIT_XPRT) { - struct tcpcheck_rule *next; + if (!cs) + rule = check->current_step; + else if (cs && (check->state & CHK_ST_CLOSE_CONN)) { + /* We are still waiting the connection gets closed */ + goto out; + } + else { + if (conn->flags & CO_FL_WAIT_XPRT) { + struct tcpcheck_rule *next; - next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step); - if (next && next->action == TCPCHK_ACT_SEND) { - if (!(check->wait_list.events & SUB_RETRY_SEND)) - conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list); - goto out; - } - else { - eval_ret = tcpcheck_eval_recv(check, check->current_step); - if (eval_ret == TCPCHK_EVAL_STOP) - goto out_end_tcpcheck; - else if (eval_ret == TCPCHK_EVAL_WAIT) + next = get_next_tcpcheck_rule(check->tcpcheck_rules, check->current_step); + if (next && next->action == TCPCHK_ACT_SEND) { + if (!(check->wait_list.events & SUB_RETRY_SEND)) + conn->mux->subscribe(cs, SUB_RETRY_SEND, &check->wait_list); goto out; - last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS))); - must_read = 0; + } + else { + eval_ret = tcpcheck_eval_recv(check, check->current_step); + if (eval_ret == TCPCHK_EVAL_STOP) + goto out_end_tcpcheck; + else if (eval_ret == TCPCHK_EVAL_WAIT) + goto out; + last_read = ((conn->flags & CO_FL_ERROR) || (cs->flags & (CS_FL_ERROR|CS_FL_EOS))); + must_read = 0; + } } - } - if (check->proxy->timeout.check) - check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check); + if (check->proxy->timeout.check) + check->task->expire = tick_add_ifset(now_ms, check->proxy->timeout.check); - rule = LIST_NEXT(&check->current_step->list, typeof(rule), list); + rule = LIST_NEXT(&check->current_step->list, typeof(rule), list); + } } /* 3- check if a rule must be resume. It happens if check->current_step @@ -2048,13 +2043,20 @@ check->code = 0; switch (rule->action) { case TCPCHK_ACT_CONNECT: + /* Not the first connection, release it first */ + if (check->cs && check->current_step != rule) { + check->state |= CHK_ST_CLOSE_CONN; + retcode = -1; + } + check->current_step = rule; - /* close but not release yet previous connection */ - if (check->cs) { - cs_close(check->cs); - retcode = -1; /* do not reuse the fd in the caller! */ + /* We are still waiting the connection gets closed */ + if (check->cs && (check->state & CHK_ST_CLOSE_CONN)) { + eval_ret = TCPCHK_EVAL_WAIT; + break; } + eval_ret = tcpcheck_eval_connect(check, rule); /* Refresh conn-stream and connection */ @@ -2779,7 +2781,7 @@ memprintf(errmsg, "'%s' expects a integer as argument", args[cur_arg]); goto error; } - /* Use an signed integer here because of chksize */ + /* Use an signed integer here because of bufsize */ cur_arg++; min_recv = atol(args[cur_arg]); if (min_recv < -1 || min_recv > INT_MAX) { diff -Nru haproxy-2.2.8/src/thread.c haproxy-2.2.9/src/thread.c --- haproxy-2.2.8/src/thread.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/thread.c 2021-02-06 16:02:45.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.2.8/src/tools.c haproxy-2.2.9/src/tools.c --- haproxy-2.2.8/src/tools.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/tools.c 2021-02-06 16:02:45.000000000 +0000 @@ -4439,7 +4439,7 @@ * The symbol's base address is returned, or NULL when unresolved, in order to * allow the caller to match it against known ones. */ -const void *resolve_sym_name(struct buffer *buf, const char *pfx, void *addr) +const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *addr) { const struct { const void *func; diff -Nru haproxy-2.2.8/src/xprt_handshake.c haproxy-2.2.9/src/xprt_handshake.c --- haproxy-2.2.8/src/xprt_handshake.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/xprt_handshake.c 2021-02-06 16:02:45.000000000 +0000 @@ -36,7 +36,8 @@ return 0; } -static struct task *xprt_handshake_io_cb(struct task *t, void *bctx, unsigned short state) +/* xprt_handshake_io_cb is exported to see it resolved in "show fd" */ +struct task *xprt_handshake_io_cb(struct task *t, void *bctx, unsigned short state) { struct xprt_handshake_ctx *ctx = bctx; struct connection *conn = ctx->conn; diff -Nru haproxy-2.2.8/src/xxhash.c haproxy-2.2.9/src/xxhash.c --- haproxy-2.2.8/src/xxhash.c 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/src/xxhash.c 2021-02-06 16:02:45.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.2.8/SUBVERS haproxy-2.2.9/SUBVERS --- haproxy-2.2.8/SUBVERS 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/SUBVERS 2021-02-06 16:02:45.000000000 +0000 @@ -1,2 +1,2 @@ --7bf78d7 +-a947cc2 diff -Nru haproxy-2.2.8/VERDATE haproxy-2.2.9/VERDATE --- haproxy-2.2.8/VERDATE 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/VERDATE 2021-02-06 16:02:45.000000000 +0000 @@ -1,2 +1,2 @@ -2021-01-13 16:10:39 +0100 -2021/01/13 +2021-02-06 17:02:45 +0100 +2021/02/06 diff -Nru haproxy-2.2.8/VERSION haproxy-2.2.9/VERSION --- haproxy-2.2.8/VERSION 2021-01-13 15:10:39.000000000 +0000 +++ haproxy-2.2.9/VERSION 2021-02-06 16:02:45.000000000 +0000 @@ -1 +1 @@ -2.2.8 +2.2.9