Package: release.debian.org
Severity: normal
User: release.debian....@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 via email to