[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#858892: unblock: haproxy/1.7.4-1



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Hey!

We would like to upload HAProxy 1.7.4 to unstable. This is mostly a
bugfix only release (1.7 is the current stable branch).

Upstream says:

> The most important ones concern a regression unveiled by a fix introduced
> in 1.7.3 (which itself allowed to spot another one), another issue where
> clients could occasionally get a 503 when compression was enabled, and a
> risk of memory leak if a redirect is enabled on http-response with compression
> enabled. [...]
>
> All users of 1.7 should definitely upgrade.

Changelog is:

    - BUG/MAJOR: connection: update CO_FL_CONNECTED before calling the data layer
    - BUG/MAJOR: http: fix typo in http_apply_redirect_rule
    - BUG/MAJOR: stream-int: do not depend on connection flags to detect connection
    - BUG/MEDIUM: cli: Prevent double free in CLI ACL lookup
    - BUG/MEDIUM: connection: ensure to always report the end of handshakes
    - BUG/MEDIUM: filters: Fix channels synchronization in flt_end_analyze
    - BUG/MEDIUM: listener: do not try to rebind another process' socket
    - BUG/MEDIUM: ssl: Clear OpenSSL error stack after trying to parse OCSP file
    - BUG/MEDIUM: ssl: switchctx should not return SSL_TLSEXT_ERR_ALERT_WARNING
    - BUG/MEDIUM: stream: fix client-fin/server-fin handling
    - BUG/MEDIUM: tcp: don't require privileges to bind to device
    - BUG/MINOR: Fix "get map <map> <value>" CLI command
    - BUG/MINOR: cfgparse: loop in tracked servers lists not detected by check_config_validity().
    - BUG/MINOR: checks: attempt clean shutw for SSL check
    - BUG/MINOR: raw_sock: always perfom the last recv if RDHUP is not available
    - BUG/MINOR: spoe: Fix parsing of arguments in spoe-message section
    - BUG/MINOR: spoe: Fix soft stop handler using a specific id for spoe filters
    - BUG: payload: fix payload not retrieving arbitrary lengths
    - BUILD: make the release script use shortlog for the final changelog
    - BUILD: scripts: fix typo in announce-release error message
    - CONTRIB: tcploop: add limits.h to fix build issue with some compilers
    - CONTRIB: tcploop: fix connect's address length
    - CONTRIB: tcploop: fix time format to silence build warnings
    - CONTRIB: tcploop: make it build on FreeBSD
    - CONTRIB: tcploop: report action 'K' (kill) in usage message
    - CONTRIB: tcploop: use the trash instead of NULL for recv()
    - DOC/MINOR: Fix typos in proxy protocol doc
    - DOC: Protocol doc: add SSL TLVs, rename CHECKSUM
    - DOC: Protocol doc: add checksum, TLV type ranges
    - DOC: Protocol doc: add noop TLV
    - MEDIUM: global: add a 'hard-stop-after' option to cap the soft-stop time
    - MINOR: config: warn when some HTTP rules are used in a TCP proxy
    - MINOR: doc: 2.4. Examples should be 2.5. Examples
    - MINOR: doc: fix use-server example (imap vs mail)
    - MINOR: fd: add a new flag HAP_POLL_F_RDHUP to struct poller
    - MINOR: server: irrelevant error message with 'default-server' config file keyword.

And diffstat:

 CHANGELOG                        |  38 ++++++++++
 README                           |   2 +-
 VERDATE                          |   2 +-
 VERSION                          |   2 +-
 contrib/tcploop/tcploop.c        |  25 ++++++-
 doc/configuration.txt            |  23 +++++-
 doc/proxy-protocol.txt           | 157 +++++++++++++++++++++++++++++++++------
 examples/haproxy.spec            |   5 +-
 include/types/channel.h          |   3 +-
 include/types/connection.h       |  10 +--
 include/types/fd.h               |   5 ++
 include/types/global.h           |   4 +
 include/types/proto_http.h       |   7 +-
 include/types/stream_interface.h |   1 +
 scripts/announce-release         |   4 +-
 src/cfgparse.c                   |  35 ++++++++-
 src/checks.c                     |  13 ++--
 src/connection.c                 |  44 +++++++----
 src/ev_epoll.c                   |   5 +-
 src/ev_kqueue.c                  |   1 +
 src/ev_poll.c                    |   1 +
 src/ev_select.c                  |   1 +
 src/filters.c                    |  52 ++++++++-----
 src/flt_spoe.c                   |  19 ++++-
 src/haproxy.c                    |   3 +
 src/listener.c                   |  10 +--
 src/map.c                        |   3 +-
 src/payload.c                    |   2 +-
 src/proto_http.c                 |  22 +++---
 src/proto_tcp.c                  |   1 -
 src/proxy.c                      |  67 +++++++++++++++++
 src/raw_sock.c                   |  14 +++-
 src/server.c                     |   2 +-
 src/ssl_sock.c                   |   4 +-
 src/stream.c                     |  10 +--
 src/stream_interface.c           |  18 ++++-
 36 files changed, 488 insertions(+), 127 deletions(-)

The attached diff is for "src/" only (21 files changed, 244
insertions, 83 deletions).

Would it be OK?

unblock haproxy/1.7.4-1

- -- System Information:
Debian Release: 9.0
  APT prefers unstable-debug
  APT policy: (500, 'unstable-debug'), (500, 'unstable'), (101, 'experimental-debug'), (101, 'experimental')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 4.9.0-2-amd64 (SMP w/4 CPU cores)
Locale: LANG=fr_FR.utf8, LC_CTYPE=fr_FR.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)

-----BEGIN PGP SIGNATURE-----

iQJGBAEBCAAwFiEErvI0h2bzccaJpzYAlaQv6DU1JfkFAljaM6oSHGJlcm5hdEBk
ZWJpYW4ub3JnAAoJEJWkL+g1NSX5q2EP/jCKPhduEXGz3DGW/vvdCjD48CsdzyxT
IuuVgNxzIt4FQ6B95o4YcfN/eQU8aG03rIytBlwYoVFZLK3oy/F4wG8hNmLFk6MN
i7EFA6EA/cQkP+5K0JFCYYehkHRxEY4E9vwf0j0kLS2fNu+r+N7ZEJ7nlLZFfLWA
6dOyUpGHnJJYspVLOm0DqSL40SqJJV1HagJUx1GeTqo0z12hojnrRTtRUnjmB8Nk
bbMu5OqRcSD4+Zqg8iPzAazlaiB7dYfb0yVKiIMhwJxB+Xo3u427BaMH9YUb36GZ
1K+gyQgY5X2aT69uJOR0W9Wmn5tT5adGXWgy+aE/0wCRS2xKKT401/h/v7yiP4Uu
qG5w6WHjFehTG08KH53X9SARnWlYVk+PmgAv9TxgWB1GxQkJCSfKnbwTrMg7AQvj
RvKO0ETkCQVo2Zc3umNwPDKbMdfUIWOg3Z8ynMcXPb6zVZFbp0jf9s/S01eev6sq
vRDsOaEml9lz7/04/3pdpmv8SkjwMaQuJVq9RAMoxz1EKzr6Q/IOV7xCQb2LQAC1
qe20RX8iDs8ecZT+wKQaaObGk7tcOty9G/BhuDte0Py9hv9SbsLTMqYqQWrujyza
tz7b+mmiEBxAkQ3zahE3v6EviTQ1GTX6A/E5GFFvqC/dC8ohIVVjQ5jpJyHXf8Tt
R6C+7pcQ6hu1
=1GeK
-----END PGP SIGNATURE-----
diff --git a/src/cfgparse.c b/src/cfgparse.c
index fc6e1497f129..074d5e672a3a 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -8552,11 +8552,12 @@ out_uri_auth_compat:
 
 				for (loop = srv->track; loop && loop != newsrv; loop = loop->track);
 
-				if (loop) {
+				if (newsrv == srv || loop) {
 					Alert("config : %s '%s', server '%s': unable to track %s/%s as it "
 					      "belongs to a tracking chain looping back to %s/%s.\n",
 					      proxy_type_str(curproxy), curproxy->id,
-					      newsrv->id, px->id, srv->id, px->id, loop->id);
+					      newsrv->id, px->id, srv->id, px->id,
+					      newsrv == srv ? srv->id : loop->id);
 					cfgerr++;
 					goto next_srv;
 				}
@@ -8681,6 +8682,36 @@ out_uri_auth_compat:
 				curproxy->uri_auth = NULL;
 			}
 
+			if (curproxy->capture_name) {
+				Warning("config : 'capture' statement ignored for %s '%s' as it requires HTTP mode.\n",
+					proxy_type_str(curproxy), curproxy->id);
+				err_code |= ERR_WARN;
+			}
+
+			if (!LIST_ISEMPTY(&curproxy->http_req_rules)) {
+				Warning("config : 'http-request' rules ignored for %s '%s' as they require HTTP mode.\n",
+					proxy_type_str(curproxy), curproxy->id);
+				err_code |= ERR_WARN;
+			}
+
+			if (!LIST_ISEMPTY(&curproxy->http_res_rules)) {
+				Warning("config : 'http-response' rules ignored for %s '%s' as they require HTTP mode.\n",
+					proxy_type_str(curproxy), curproxy->id);
+				err_code |= ERR_WARN;
+			}
+
+			if (!LIST_ISEMPTY(&curproxy->block_rules)) {
+				Warning("config : 'block' rules ignored for %s '%s' as they require HTTP mode.\n",
+					proxy_type_str(curproxy), curproxy->id);
+				err_code |= ERR_WARN;
+			}
+
+			if (!LIST_ISEMPTY(&curproxy->redirect_rules)) {
+				Warning("config : 'redirect' rules ignored for %s '%s' as they require HTTP mode.\n",
+					proxy_type_str(curproxy), curproxy->id);
+				err_code |= ERR_WARN;
+			}
+
 			if (curproxy->options & (PR_O_FWDFOR | PR_O_FF_ALWAYS)) {
 				Warning("config : 'option %s' ignored for %s '%s' as it requires HTTP mode.\n",
 					"forwardfor", proxy_type_str(curproxy), curproxy->id);
diff --git a/src/checks.c b/src/checks.c
index 1e43dec3f02a..2d1447c71a36 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -1355,14 +1355,15 @@ static void event_srv_chk_r(struct connection *conn)
 	*check->bi->data = '\0';
 	check->bi->i = 0;
 
-	/* Close the connection... We absolutely want to perform a hard close
-	 * and reset the connection if some data are pending, otherwise we end
-	 * up with many TIME_WAITs and eat all the source port range quickly.
-	 * To avoid sending RSTs all the time, we first try to drain pending
-	 * data.
+	/* Close the connection... We still attempt to nicely close if,
+	 * for instance, SSL needs to send a "close notify." Later, we perform
+	 * a hard close and reset the connection if some data are pending,
+	 * otherwise we end up with many TIME_WAITs and eat all the source port
+	 * range quickly.  To avoid sending RSTs all the time, we first try to
+	 * drain pending data.
 	 */
 	__conn_data_stop_both(conn);
-	conn_data_shutw_hard(conn);
+	conn_data_shutw(conn);
 
 	/* OK, let's not stay here forever */
 	if (check->result == CHK_RES_FAILED)
diff --git a/src/connection.c b/src/connection.c
index 26fc5f6839fc..362909464257 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -99,19 +99,21 @@ void conn_fd_handler(int fd)
 	 */
 	if (conn->xprt && fd_recv_ready(fd) &&
 	    ((conn->flags & (CO_FL_DATA_RD_ENA|CO_FL_WAIT_ROOM|CO_FL_ERROR|CO_FL_HANDSHAKE)) == CO_FL_DATA_RD_ENA)) {
-		/* force detection of a flag change : it's impossible to have both
-		 * CONNECTED and WAIT_CONN so we're certain to trigger a change.
+		/* force reporting of activity by clearing the previous flags :
+		 * we'll have at least ERROR or CONNECTED at the end of an I/O,
+		 * both of which will be detected below.
 		 */
-		flags = CO_FL_WAIT_L4_CONN | CO_FL_CONNECTED;
+		flags = 0;
 		conn->data->recv(conn);
 	}
 
 	if (conn->xprt && fd_send_ready(fd) &&
 	    ((conn->flags & (CO_FL_DATA_WR_ENA|CO_FL_WAIT_DATA|CO_FL_ERROR|CO_FL_HANDSHAKE)) == CO_FL_DATA_WR_ENA)) {
-		/* force detection of a flag change : it's impossible to have both
-		 * CONNECTED and WAIT_CONN so we're certain to trigger a change.
+		/* force reporting of activity by clearing the previous flags :
+		 * we'll have at least ERROR or CONNECTED at the end of an I/O,
+		 * both of which will be detected below.
 		 */
-		flags = CO_FL_WAIT_L4_CONN | CO_FL_CONNECTED;
+		flags = 0;
 		conn->data->send(conn);
 	}
 
@@ -129,21 +131,33 @@ void conn_fd_handler(int fd)
 		if (!tcp_connect_probe(conn))
 			goto leave;
 	}
-
  leave:
-	/* The wake callback may be used to process a critical error and abort the
-	 * connection. If so, we don't want to go further as the connection will
-	 * have been released and the FD destroyed.
+	/* Verify if the connection just established. */
+	if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED))))
+		conn->flags |= CO_FL_CONNECTED;
+
+	/* The wake callback is normally used to notify the data layer about
+	 * data layer activity (successful send/recv), connection establishment,
+	 * shutdown and fatal errors. We need to consider the following
+	 * situations to wake up the data layer :
+	 *  - change among the CO_FL_NOTIFY_DATA flags :
+	 *      {DATA,SOCK}_{RD,WR}_SH, ERROR,
+	 *  - absence of any of {L4,L6}_CONN and CONNECTED, indicating the
+	 *    end of handshake and transition to CONNECTED
+	 *  - raise of CONNECTED with HANDSHAKE down
+	 *  - end of HANDSHAKE with CONNECTED set
+	 *  - regular data layer activity
+	 *
+	 * Note that the wake callback is allowed to release the connection and
+	 * the fd (and return < 0 in this case).
 	 */
 	if ((conn->flags & CO_FL_WAKE_DATA) &&
-	    ((conn->flags ^ flags) & CO_FL_CONN_STATE) &&
+	    (((conn->flags ^ flags) & CO_FL_NOTIFY_DATA) ||
+	     ((flags & (CO_FL_CONNECTED|CO_FL_HANDSHAKE)) != CO_FL_CONNECTED &&
+	      (conn->flags & (CO_FL_CONNECTED|CO_FL_HANDSHAKE)) == CO_FL_CONNECTED)) &&
 	    conn->data->wake(conn) < 0)
 		return;
 
-	/* Last check, verify if the connection just established */
-	if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED))))
-		conn->flags |= CO_FL_CONNECTED;
-
 	/* remove the events before leaving */
 	fdtab[fd].ev &= FD_POLL_STICKY;
 
diff --git a/src/ev_epoll.c b/src/ev_epoll.c
index ccb7c33772ab..eb2bceb0383f 100644
--- a/src/ev_epoll.c
+++ b/src/ev_epoll.c
@@ -154,8 +154,10 @@ REGPRM2 static void _do_poll(struct poller *p, int exp)
 		}
 
 		/* always remap RDHUP to HUP as they're used similarly */
-		if (e & EPOLLRDHUP)
+		if (e & EPOLLRDHUP) {
+			cur_poller.flags |= HAP_POLL_F_RDHUP;
 			n |= FD_POLL_HUP;
+		}
 
 		fdtab[fd].ev |= n;
 		if (n & (FD_POLL_IN | FD_POLL_HUP | FD_POLL_ERR))
@@ -263,6 +265,7 @@ static void _do_register(void)
 
 	p->name = "epoll";
 	p->pref = 300;
+	p->flags = 0;
 	p->private = NULL;
 
 	p->clo  = __fd_clo;
diff --git a/src/ev_kqueue.c b/src/ev_kqueue.c
index 081e78aa7757..de7da655b26c 100644
--- a/src/ev_kqueue.c
+++ b/src/ev_kqueue.c
@@ -234,6 +234,7 @@ static void _do_register(void)
 
 	p->name = "kqueue";
 	p->pref = 300;
+	p->flags = 0;
 	p->private = NULL;
 
 	p->clo  = NULL;
diff --git a/src/ev_poll.c b/src/ev_poll.c
index 80d88eb91e48..9a6faa9bf718 100644
--- a/src/ev_poll.c
+++ b/src/ev_poll.c
@@ -235,6 +235,7 @@ static void _do_register(void)
 
 	p->name = "poll";
 	p->pref = 200;
+	p->flags = 0;
 	p->private = NULL;
 
 	p->clo  = __fd_clo;
diff --git a/src/ev_select.c b/src/ev_select.c
index 35d3c77db7a6..1b40ea1028a1 100644
--- a/src/ev_select.c
+++ b/src/ev_select.c
@@ -235,6 +235,7 @@ static void _do_register(void)
 
 	p->name = "select";
 	p->pref = 150;
+	p->flags = 0;
 	p->private = NULL;
 
 	p->clo  = __fd_clo;
diff --git a/src/filters.c b/src/filters.c
index aa6e8bb94a9f..3d5564543dab 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -675,6 +675,9 @@ flt_start_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
 	/* If this function is called, this means there is at least one filter,
 	 * so we do not need to check the filter list's emptiness. */
 
+	/* Set flag on channel to tell that the channel is filtered */
+	chn->flags |= CF_FLT_ANALYZE;
+
 	RESUME_FILTER_LOOP(s, chn) {
 		if (!(chn->flags & CF_ISRESP)) {
 			if (an_bit == AN_REQ_FLT_START_BE &&
@@ -801,6 +804,11 @@ flt_end_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
 {
 	int ret = 1;
 
+	/* Check if all filters attached on the stream have finished their
+	 * processing on this channel. */
+	if (!(chn->flags & CF_FLT_ANALYZE))
+		goto sync;
+
 	RESUME_FILTER_LOOP(s, chn) {
 		FLT_NXT(filter, chn) = 0;
 		FLT_FWD(filter, chn) = 0;
@@ -813,27 +821,31 @@ flt_end_analyze(struct stream *s, struct channel *chn, unsigned int an_bit)
 		}
 	} RESUME_FILTER_END;
 
-end:
-	ret = handle_analyzer_result(s, chn, an_bit, ret);
-
-	/* Check if 'channel_end_analyze' callback has been called for the
-	 * request and the response. */
-	if (!(s->req.analysers & AN_REQ_FLT_END) && !(s->res.analysers & AN_RES_FLT_END)) {
-		/* When we are waiting for a new request, so we must reset
-		 * stream analyzers. The input must not be closed the request
-		 * channel, else it is useless to wait. */
-		if (s->txn && (s->txn->flags & TX_WAIT_NEXT_RQ) && !channel_input_closed(&s->req)) {
-			s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0;
-			s->res.analysers = 0;
-
-			/* Remove backend filters from the list */
-			flt_stream_release(s, 1);
-		}
-
+ end:
+	/* We don't remove yet this analyzer because we need to synchronize the
+	 * both channels. So here, we just remove the flag CF_FLT_ANALYZE. */
+	ret = handle_analyzer_result(s, chn, 0, ret);
+	if (ret)
+		chn->flags &= ~CF_FLT_ANALYZE;
+
+ sync:
+	/* Now we can check if filters have finished their work on the both
+	 * channels */
+	if (!(s->req.flags & CF_FLT_ANALYZE) && !(s->res.flags & CF_FLT_ANALYZE)) {
+		/* Sync channels by removing this analyzer for the both channels */
+		s->req.analysers &= ~AN_REQ_FLT_END;
+		s->res.analysers &= ~AN_RES_FLT_END;
+
+		/* Clean up the HTTP transaction if needed */
+		if (s->txn && (s->txn->flags & TX_WAIT_CLEANUP))
+			http_end_txn_clean_session(s);
+
+		/* Remove backend filters from the list */
+		flt_stream_release(s, 1);
 	}
-	else if (ret) {
-		/* Analyzer ends only for one channel. So wake up the stream to
-		 * be sure to process it for the other side as soon as
+	else {
+		/* This analyzer ends only for one channel. So wake up the
+		 * stream to be sure to process it for the other side as soon as
 		 * possible. */
 		task_wakeup(s->task, TASK_WOKEN_MSG);
 	}
diff --git a/src/flt_spoe.c b/src/flt_spoe.c
index aa6414abfdf4..57fb1da12e4f 100644
--- a/src/flt_spoe.c
+++ b/src/flt_spoe.c
@@ -235,6 +235,9 @@ struct spoe_context {
 	unsigned int        process_exp;  /* expiration date to process an event */
 };
 
+/* SPOE filter id. Used to identify SPOE filters */
+const char *spoe_filter_id = "SPOE filter";
+
 /* Set if the handle on SIGUSR1 is registered */
 static int sighandler_registered = 0;
 
@@ -2286,10 +2289,16 @@ sig_stop_spoe(struct sig_handler *sh)
 		struct flt_conf *fconf;
 
 		list_for_each_entry(fconf, &p->filter_configs, list) {
-			struct spoe_config *conf  = fconf->conf;
-			struct spoe_agent  *agent = conf->agent;
+			struct spoe_config *conf;
+			struct spoe_agent  *agent;
 			struct appctx      *appctx;
 
+			if (fconf->id != spoe_filter_id)
+				continue;
+
+			conf  = fconf->conf;
+			agent = conf->agent;
+
 			list_for_each_entry(appctx, &agent->cache, ctx.spoe.list) {
 				si_applet_want_get(appctx->owner);
 				si_applet_want_put(appctx->owner);
@@ -2957,8 +2966,9 @@ cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
 				arg->name_len = delim - args[cur_arg];
 				delim++;
 			}
-
-			arg->expr = sample_parse_expr(&delim, &idx, file, linenum, &errmsg, &curproxy->conf.args);
+			arg->expr = sample_parse_expr((char*[]){delim, NULL},
+						      &idx, file, linenum, &errmsg,
+						      &curproxy->conf.args);
 			if (arg->expr == NULL) {
 				Alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
 				err_code |= ERR_ALERT | ERR_FATAL;
@@ -3178,6 +3188,7 @@ parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
 	}
 
 	*cur_arg    = pos;
+	fconf->id   = spoe_filter_id;
 	fconf->ops  = &spoe_ops;
 	fconf->conf = conf;
 	return 0;
diff --git a/src/haproxy.c b/src/haproxy.c
index 449ab054f48a..a58008e44aac 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -140,6 +140,7 @@ int  relative_pid = 1;		/* process id starting at 1 */
 
 /* global options */
 struct global global = {
+	.hard_stop_after = TICK_ETERNITY,
 	.nbproc = 1,
 	.req_count = 0,
 	.logsrvs = LIST_HEAD_INIT(global.logsrvs),
@@ -228,6 +229,7 @@ struct global global = {
 /*********************************************************************/
 
 int stopping;	/* non zero means stopping in progress */
+int killed;	/* non zero means a hard-stop is triggered */
 int jobs = 0;   /* number of active jobs (conns, listeners, active tasks, ...) */
 
 /* Here we store informations about the pids of the processes we may pause
@@ -708,6 +710,7 @@ void init(int argc, char **argv)
 	 */
     
 	totalconn = actconn = maxfd = listeners = stopping = 0;
+	killed = 0;
     
 
 #ifdef HAPROXY_MEMMAX
diff --git a/src/listener.c b/src/listener.c
index c2ce41329ce7..3c043baad339 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -128,6 +128,11 @@ int pause_listener(struct listener *l)
  */
 int resume_listener(struct listener *l)
 {
+	if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
+	    l->bind_conf->bind_proc &&
+	    !(l->bind_conf->bind_proc & (1UL << (relative_pid - 1))))
+		return 1;
+
 	if (l->state == LI_ASSIGNED) {
 		char msg[100];
 		int err;
@@ -145,11 +150,6 @@ int resume_listener(struct listener *l)
 	if (l->state < LI_PAUSED)
 		return 0;
 
-	if ((global.mode & (MODE_DAEMON | MODE_SYSTEMD)) &&
-	    l->bind_conf->bind_proc &&
-	    !(l->bind_conf->bind_proc & (1UL << (relative_pid - 1))))
-		return 1;
-
 	if (l->proto->sock_prot == IPPROTO_TCP &&
 	    l->state == LI_PAUSED &&
 	    listen(l->fd, l->backlog ? l->backlog : l->maxconn) != 0)
diff --git a/src/map.c b/src/map.c
index b6fce4df84b3..e8c6a2acbf05 100644
--- a/src/map.c
+++ b/src/map.c
@@ -524,7 +524,6 @@ static int cli_io_handler_map_lookup(struct appctx *appctx)
 
 	default:
 		appctx->st2 = STAT_ST_FIN;
-		free(appctx->ctx.map.chunk.str);
 		return 1;
 	}
 }
@@ -907,7 +906,7 @@ static struct cli_kw_list cli_kws = {{ },{
 	{ { "add",   "map", NULL }, "add map        : add map entry", cli_parse_add_map, NULL },
 	{ { "clear", "map", NULL }, "clear map <id> : clear the content of this map", cli_parse_clear_map, NULL },
 	{ { "del",   "map", NULL }, "del map        : delete map entry", cli_parse_del_map, NULL },
-	{ { "get",   "map", NULL }, "get map        : report the keys and values matching a sample for a map", cli_parse_get_map, NULL },
+	{ { "get",   "map", NULL }, "get map        : report the keys and values matching a sample for a map", cli_parse_get_map, cli_io_handler_map_lookup, cli_release_mlook },
 	{ { "set",   "map", NULL }, "set map        : modify map entry", cli_parse_set_map, NULL },
 	{ { "show",  "map", NULL }, "show map [id]  : report available maps or dump a map's contents", cli_parse_show_map, NULL },
 	{ { NULL }, NULL, NULL, NULL }
diff --git a/src/payload.c b/src/payload.c
index a02a86966051..b80a19c91909 100644
--- a/src/payload.c
+++ b/src/payload.c
@@ -838,7 +838,7 @@ smp_fetch_payload(const struct arg *arg_p, struct sample *smp, const char *kw, v
 		return 0;
 
 	chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
-	if (!buf_size || buf_size > global.tune.bufsize || buf_offset + buf_size > global.tune.bufsize) {
+	if (buf_size > global.tune.bufsize || buf_offset + buf_size > global.tune.bufsize) {
 		/* will never match */
 		smp->flags = 0;
 		return 0;
diff --git a/src/proto_http.c b/src/proto_http.c
index eb53b7235a3a..f4a57e975c7c 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -4253,7 +4253,7 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s
 		req->next -= req->sov;
 		req->sov = 0;
 		s->req.analysers = AN_REQ_HTTP_XFER_BODY | (s->req.analysers & AN_REQ_FLT_END);
-		s->res.analysers = AN_RES_HTTP_XFER_BODY | (s->req.analysers & AN_RES_FLT_END);
+		s->res.analysers = AN_RES_HTTP_XFER_BODY | (s->res.analysers & AN_RES_FLT_END);
 		req->msg_state = HTTP_MSG_CLOSED;
 		res->msg_state = HTTP_MSG_DONE;
 		/* Trim any possible response */
@@ -5310,15 +5310,8 @@ void http_end_txn_clean_session(struct stream *s)
 		else
 			si_idle_conn(&s->si[1], &srv->idle_conns);
 	}
-
-	if (HAS_FILTERS(s)) {
-		s->req.analysers &= AN_REQ_FLT_END;
-		s->res.analysers &= AN_RES_FLT_END;
-	}
-	else {
-		s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0;
-		s->res.analysers = 0;
-	}
+	s->req.analysers = strm_li(s) ? strm_li(s)->analysers : 0;
+	s->res.analysers = 0;
 }
 
 
@@ -5674,8 +5667,12 @@ int http_resync_states(struct stream *s)
 			s->req.flags |= CF_WAKE_WRITE;
 		else if (channel_congested(&s->res))
 			s->res.flags |= CF_WAKE_WRITE;
-		else
-			http_end_txn_clean_session(s);
+		else {
+			s->req.analysers = AN_REQ_FLT_END;
+			s->res.analysers = AN_RES_FLT_END;
+			txn->flags |= TX_WAIT_CLEANUP;
+			return 1;
+		}
 	}
 
 	return txn->req.msg_state != old_req_state ||
@@ -9007,6 +9004,7 @@ void http_reset_txn(struct stream *s)
 	s->res.rex = TICK_ETERNITY;
 	s->res.wex = TICK_ETERNITY;
 	s->res.analyse_exp = TICK_ETERNITY;
+	s->si[1].hcto = TICK_ETERNITY;
 }
 
 void free_http_res_rules(struct list *r)
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index c04f2767f0ef..a2bb9d71cab8 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -1698,7 +1698,6 @@ static int bind_parse_interface(char **args, int cur_arg, struct proxy *px, stru
 			l->interface = strdup(args[cur_arg + 1]);
 	}
 
-	global.last_checks |= LSTCHK_NETADM;
 	return 0;
 }
 #endif
diff --git a/src/proxy.c b/src/proxy.c
index a84a08fee23f..78120d9bc813 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -913,6 +913,58 @@ struct task *manage_proxy(struct task *t)
 }
 
 
+static int proxy_parse_hard_stop_after(char **args, int section_type, struct proxy *curpx,
+                                struct proxy *defpx, const char *file, int line,
+                                char **err)
+{
+	const char *res;
+
+	if (!*args[1]) {
+		memprintf(err, "'%s' expects <time> as argument.\n", args[0]);
+		return -1;
+	}
+	res = parse_time_err(args[1], &global.hard_stop_after, TIME_UNIT_MS);
+	if (res) {
+		memprintf(err, "unexpected character '%c' in argument to <%s>.\n", *res, args[0]);
+		return -1;
+	}
+	return 0;
+}
+
+struct task *hard_stop(struct task *t)
+{
+	struct proxy *p;
+	struct stream *s;
+
+	if (killed) {
+		Warning("Some tasks resisted to hard-stop, exiting now.\n");
+		send_log(NULL, LOG_WARNING, "Some tasks resisted to hard-stop, exiting now.\n");
+		/* Do some cleanup and explicitely quit */
+		deinit();
+		exit(0);
+	}
+
+	Warning("soft-stop running for too long, performing a hard-stop.\n");
+	send_log(NULL, LOG_WARNING, "soft-stop running for too long, performing a hard-stop.\n");
+	p = proxy;
+	while (p) {
+		if ((p->cap & PR_CAP_FE) && (p->feconn > 0)) {
+			Warning("Proxy %s hard-stopped (%d remaining conns will be closed).\n",
+				p->id, p->feconn);
+			send_log(p, LOG_WARNING, "Proxy %s hard-stopped (%d remaining conns will be closed).\n",
+				p->id, p->feconn);
+		}
+		p = p->next;
+	}
+	list_for_each_entry(s, &streams, list) {
+		stream_shutdown(s, SF_ERR_KILLED);
+	}
+
+	killed = 1;
+	t->expire = tick_add(now_ms, MS_TO_TICKS(1000));
+	return t;
+}
+
 /*
  * this function disables health-check servers so that the process will quickly be ignored
  * by load balancers. Note that if a proxy was already in the PAUSED state, then its grace
@@ -922,8 +974,19 @@ void soft_stop(void)
 {
 	struct proxy *p;
 	struct peers *prs;
+	struct task *task;
 
 	stopping = 1;
+	if (tick_isset(global.hard_stop_after)) {
+		task = task_new();
+		if (task) {
+			task->process = hard_stop;
+			task_schedule(task, tick_add(now_ms, global.hard_stop_after));
+		}
+		else {
+			Alert("out of memory trying to allocate the hard-stop task.\n");
+		}
+	}
 	p = proxy;
 	tv_update_date(0,1); /* else, the old time before select will be used */
 	while (p) {
@@ -1151,6 +1214,9 @@ int stream_set_backend(struct stream *s, struct proxy *be)
 	if (be->options2 & PR_O2_INDEPSTR)
 		s->si[1].flags |= SI_FL_INDEP_STR;
 
+	if (tick_isset(be->timeout.serverfin))
+		s->si[1].hcto = be->timeout.serverfin;
+
 	/* We want to enable the backend-specific analysers except those which
 	 * were already run as part of the frontend/listener. Note that it would
 	 * be more reliable to store the list of analysers that have been run,
@@ -1211,6 +1277,7 @@ int stream_set_backend(struct stream *s, struct proxy *be)
 }
 
 static struct cfg_kw_list cfg_kws = {ILH, {
+	{ CFG_GLOBAL, "hard-stop-after", proxy_parse_hard_stop_after },
 	{ CFG_LISTEN, "timeout", proxy_parse_timeout },
 	{ CFG_LISTEN, "clitimeout", proxy_parse_timeout },
 	{ CFG_LISTEN, "contimeout", proxy_parse_timeout },
diff --git a/src/raw_sock.c b/src/raw_sock.c
index 0883c57a6a45..5f171c7a6f23 100644
--- a/src/raw_sock.c
+++ b/src/raw_sock.c
@@ -296,13 +296,21 @@ static int raw_sock_to_buf(struct connection *conn, struct buffer *buf, int coun
 			if (ret < try) {
 				/* unfortunately, on level-triggered events, POLL_HUP
 				 * is generally delivered AFTER the system buffer is
-				 * empty, so this one might never match.
+				 * empty, unless the poller supports POLL_RDHUP. If
+				 * we know this is the case, we don't try to read more
+				 * as we know there's no more available. Similarly, if
+				 * there's no problem with lingering we don't even try
+				 * to read an unlikely close from the client since we'll
+				 * close first anyway.
 				 */
 				if (fdtab[conn->t.sock.fd].ev & FD_POLL_HUP)
 					goto read0;
 
-				fd_done_recv(conn->t.sock.fd);
-				break;
+				if ((!fdtab[conn->t.sock.fd].linger_risk) ||
+				    (cur_poller.flags & HAP_POLL_F_RDHUP)) {
+					fd_done_recv(conn->t.sock.fd);
+					break;
+				}
 			}
 			count -= ret;
 		}
diff --git a/src/server.c b/src/server.c
index bae9709aa34f..38619b7431a5 100644
--- a/src/server.c
+++ b/src/server.c
@@ -951,7 +951,7 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
 		else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			err_code |= ERR_ALERT | ERR_FATAL;
 
-		if (!*args[2]) {
+		if (!defsrv && !*args[2]) {
 			Alert("parsing [%s:%d] : '%s' expects <name> and <addr>[:<port>] as arguments.\n",
 			      file, linenum, args[0]);
 			err_code |= ERR_ALERT | ERR_FATAL;
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index cc2dc1283fa2..a9a8f06e4b04 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -433,6 +433,8 @@ static int ssl_sock_load_ocsp_response(struct chunk *ocsp_response, struct certi
 
 	ret = 0;
 out:
+	ERR_clear_error();
+
 	if (bs)
 		 OCSP_BASICRESP_free(bs);
 
@@ -1464,7 +1466,7 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct bind_conf *s)
 		}
 		return (s->strict_sni ?
 			SSL_TLSEXT_ERR_ALERT_FATAL :
-			SSL_TLSEXT_ERR_ALERT_WARNING);
+			SSL_TLSEXT_ERR_OK);
 	}
 
 	/* switch ctx */
diff --git a/src/stream.c b/src/stream.c
index 01a2de093f81..dd74aed1d12d 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -168,6 +168,7 @@ struct stream *stream_new(struct session *sess, struct task *t, enum obj_type *o
 	/* this part should be common with other protocols */
 	si_reset(&s->si[0]);
 	si_set_state(&s->si[0], SI_ST_EST);
+	s->si[0].hcto = sess->fe->timeout.clientfin;
 
 	/* attach the incoming connection to the stream interface now. */
 	if (conn)
@@ -182,6 +183,7 @@ struct stream *stream_new(struct session *sess, struct task *t, enum obj_type *o
 	 * callbacks will be initialized before attempting to connect.
 	 */
 	si_reset(&s->si[1]);
+	s->si[1].hcto = TICK_ETERNITY;
 
 	if (likely(sess->fe->options2 & PR_O2_INDEPSTR))
 		s->si[1].flags |= SI_FL_INDEP_STR;
@@ -2056,10 +2058,6 @@ struct task *process_stream(struct task *t)
 		if (req->flags & CF_READ_ERROR)
 			si_b->flags |= SI_FL_NOLINGER;
 		si_shutw(si_b);
-		if (tick_isset(s->be->timeout.serverfin)) {
-			res->rto = s->be->timeout.serverfin;
-			res->rex = tick_add(now_ms, res->rto);
-		}
 	}
 
 	/* shutdown(write) done on server side, we must stop the client too */
@@ -2239,10 +2237,6 @@ struct task *process_stream(struct task *t)
 	if (unlikely((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) == CF_SHUTW_NOW &&
 		     channel_is_empty(res))) {
 		si_shutw(si_f);
-		if (tick_isset(sess->fe->timeout.clientfin)) {
-			req->rto = sess->fe->timeout.clientfin;
-			req->rex = tick_add(now_ms, req->rto);
-		}
 	}
 
 	/* shutdown(write) done on the client side, we must stop the server too */
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 758aec7c7309..836487bdcc06 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -205,6 +205,11 @@ static void stream_int_shutw(struct stream_interface *si)
 	oc->wex = TICK_ETERNITY;
 	si->flags &= ~SI_FL_WAIT_DATA;
 
+	if (tick_isset(si->hcto)) {
+		ic->rto = si->hcto;
+		ic->rex = tick_add(now_ms, ic->rto);
+	}
+
 	switch (si->state) {
 	case SI_ST_EST:
 		/* we have to shut before closing, otherwise some short messages
@@ -563,7 +568,8 @@ static int si_conn_wake_cb(struct connection *conn)
 	if (conn->flags & CO_FL_ERROR)
 		si->flags |= SI_FL_ERR;
 
-	if (unlikely(!(conn->flags & (CO_FL_WAIT_L4_CONN | CO_FL_WAIT_L6_CONN | CO_FL_CONNECTED)))) {
+	if ((si->state < SI_ST_EST) &&
+	    (conn->flags & (CO_FL_CONNECTED | CO_FL_HANDSHAKE)) == CO_FL_CONNECTED) {
 		si->exp = TICK_ETERNITY;
 		oc->flags |= CF_WRITE_NULL;
 	}
@@ -825,6 +831,11 @@ static void stream_int_shutw_conn(struct stream_interface *si)
 	oc->wex = TICK_ETERNITY;
 	si->flags &= ~SI_FL_WAIT_DATA;
 
+	if (tick_isset(si->hcto)) {
+		ic->rto = si->hcto;
+		ic->rex = tick_add(now_ms, ic->rto);
+	}
+
 	switch (si->state) {
 	case SI_ST_EST:
 		/* we have to shut before closing, otherwise some short messages
@@ -1440,6 +1451,11 @@ static void stream_int_shutw_applet(struct stream_interface *si)
 	oc->wex = TICK_ETERNITY;
 	si->flags &= ~SI_FL_WAIT_DATA;
 
+	if (tick_isset(si->hcto)) {
+		ic->rto = si->hcto;
+		ic->rex = tick_add(now_ms, ic->rto);
+	}
+
 	/* on shutw we always wake the applet up */
 	appctx_wakeup(si_appctx(si));
 

Reply to: