Hi,

the attached diff fixes filter rules with "forward to" statement in
persistent (keep-alive) connections.  See the XXX comment below.

```relayd.conf
log connection
table <a> {
        127.0.0.1
}
table <b> {
        127.0.0.1
}
table <c> {
        127.0.0.1
}
http protocol pathfwd {
        return error

        # XXX The following workaround is not needed anymore:
        #match header set "Connection" value "close"

        pass path "/a/*" forward to <a>
        pass path "/b/*" forward to <b>
        #match request path log "*"
}
relay pathfwd {
        listen on 0.0.0.0 port 80
        protocol pathfwd
        forward to <c> port 8082
        forward to <a> port 8080
        forward to <b> port 8081
}
```

OK?

reyk

Index: usr.sbin/relayd/relay.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.242
diff -u -p -u -p -r1.242 relay.c
--- usr.sbin/relayd/relay.c     4 Mar 2019 21:25:03 -0000       1.242
+++ usr.sbin/relayd/relay.c     8 May 2019 14:26:40 -0000
@@ -76,11 +76,14 @@ int          relay_tls_ctx_create(struct relay 
 void            relay_tls_transaction(struct rsession *,
                    struct ctl_relay_event *);
 void            relay_tls_handshake(int, short, void *);
-void            relay_connect_retry(int, short, void *);
 void            relay_tls_connected(struct ctl_relay_event *);
 void            relay_tls_readcb(int, short, void *);
 void            relay_tls_writecb(int, short, void *);
 
+void            relay_connect_retry(int, short, void *);
+void            relay_connect_state(struct rsession *,
+                   struct ctl_relay_event *, enum relay_state);
+
 extern void     bufferevent_read_pressure_cb(struct evbuffer *, size_t,
                    size_t, void *);
 
@@ -654,6 +657,7 @@ relay_socket_listen(struct sockaddr_stor
 void
 relay_connected(int fd, short sig, void *arg)
 {
+       char                     obuf[128];
        struct rsession         *con = arg;
        struct relay            *rlay = con->se_relay;
        struct protocol         *proto = rlay->rl_proto;
@@ -696,6 +700,22 @@ relay_connected(int fd, short sig, void 
 
        DPRINTF("%s: session %d: successful", __func__, con->se_id);
 
+       /* Log destination if it was changed in a keep-alive connection */
+       if ((con->se_table != con->se_table0) &&
+           (env->sc_conf.opts & (RELAYD_OPT_LOGCON|RELAYD_OPT_LOGCONERR))) {
+               con->se_table0 = con->se_table;
+               memset(&obuf, 0, sizeof(obuf));
+               (void)print_host(&con->se_out.ss, obuf, sizeof(obuf));
+               if (asprintf(&msg, " -> %s:%d",
+                   obuf, ntohs(con->se_out.port)) == -1) {
+                       relay_abort_http(con, 500,
+                           "connection changed and asprintf failed", 0);
+                       return;
+               }
+               relay_log(con, msg);
+               free(msg);
+       }
+
        switch (rlay->rl_proto->type) {
        case RELAY_PROTO_HTTP:
                if (relay_httpdesc_init(out) == -1) {
@@ -1465,6 +1485,17 @@ relay_bindany(int fd, short event, void 
 }
 
 void
+relay_connect_state(struct rsession *con, struct ctl_relay_event *cre,
+    enum relay_state new)
+{
+       DPRINTF("%s: session %d: %s state %s -> %s",
+           __func__, con->se_id,
+           cre->dir == RELAY_DIR_REQUEST ? "accept" : "connect",
+           relay_state(cre->state), relay_state(new));
+       cre->state = new;
+}
+
+void
 relay_connect_retry(int fd, short sig, void *arg)
 {
        struct timeval   evtpause = { 1, 0 };
@@ -1533,9 +1564,9 @@ relay_connect_retry(int fd, short sig, v
        }
 
        if (rlay->rl_conf.flags & F_TLSINSPECT)
-               con->se_out.state = STATE_PRECONNECT;
+               relay_connect_state(con, &con->se_out, STATE_PRECONNECT);
        else
-               con->se_out.state = STATE_CONNECTED;
+               relay_connect_state(con, &con->se_out, STATE_CONNECTED);
        relay_inflight--;
        DPRINTF("%s: inflight decremented, now %d",__func__, relay_inflight);
 
@@ -1560,7 +1591,7 @@ relay_preconnect(struct rsession *con)
            con->se_id, privsep_process);
        rv = relay_connect(con);
        if (con->se_out.state == STATE_CONNECTED)
-               con->se_out.state = STATE_PRECONNECT;
+               relay_connect_state(con, &con->se_out, STATE_PRECONNECT);
        return (rv);
 }
 
@@ -1585,7 +1616,7 @@ relay_connect(struct rsession *con)
                        return (-1);
                }
                relay_connected(con->se_out.s, EV_WRITE, con);
-               con->se_out.state = STATE_CONNECTED;
+               relay_connect_state(con, &con->se_out, STATE_CONNECTED);
                return (0);
        }
 
@@ -1642,7 +1673,7 @@ relay_connect(struct rsession *con)
                        evtimer_add(&rlay->rl_evt, &evtpause);
 
                        /* this connect is pending */
-                       con->se_out.state = STATE_PENDING;
+                       relay_connect_state(con, &con->se_out, STATE_PENDING);
                        return (0);
                } else {
                        if (con->se_retry) {
@@ -1660,7 +1691,7 @@ relay_connect(struct rsession *con)
                }
        }
 
-       con->se_out.state = STATE_CONNECTED;
+       relay_connect_state(con, &con->se_out, STATE_CONNECTED);
        relay_inflight--;
        DPRINTF("%s: inflight decremented, now %d",__func__,
            relay_inflight);
@@ -1686,10 +1717,6 @@ relay_close(struct rsession *con, const 
        relay_session_unpublish(con);
 
        event_del(&con->se_ev);
-       if (con->se_in.bev != NULL)
-               bufferevent_disable(con->se_in.bev, EV_READ|EV_WRITE);
-       if (con->se_out.bev != NULL)
-               bufferevent_disable(con->se_out.bev, EV_READ|EV_WRITE);
 
        if ((env->sc_conf.opts & (RELAYD_OPT_LOGCON|RELAYD_OPT_LOGCONERR)) &&
            msg != NULL) {
@@ -1726,7 +1753,8 @@ relay_close(struct rsession *con, const 
 
        free(con->se_priv);
 
-       if (relay_reset_event(&con->se_in)) {
+       relay_connect_state(con, &con->se_in, STATE_DONE);
+       if (relay_reset_event(con, &con->se_in)) {
                if (con->se_out.s == -1) {
                        /*
                         * the output was never connected,
@@ -1740,7 +1768,8 @@ relay_close(struct rsession *con, const 
        if (con->se_in.output != NULL)
                evbuffer_free(con->se_in.output);
 
-       if (relay_reset_event(&con->se_out)) {
+       relay_connect_state(con, &con->se_out, STATE_DONE);
+       if (relay_reset_event(con, &con->se_out)) {
                /* Some file descriptors are available again. */
                if (evtimer_pending(&rlay->rl_evt, NULL)) {
                        evtimer_del(&rlay->rl_evt);
@@ -1766,14 +1795,16 @@ relay_close(struct rsession *con, const 
 }
 
 int
-relay_reset_event(struct ctl_relay_event *cre)
+relay_reset_event(struct rsession *con, struct ctl_relay_event *cre)
 {
        int              rv = 0;
 
-       DPRINTF("%s: state %d dir %d", __func__, cre->state, cre->dir);
-
-       if (cre->bev != NULL)
+       if (cre->state != STATE_DONE)
+               relay_connect_state(con, cre, STATE_CLOSED);
+       if (cre->bev != NULL) {
+               bufferevent_disable(cre->bev, EV_READ|EV_WRITE);
                bufferevent_free(cre->bev);
+       }
        if (cre->tls != NULL)
                tls_close(cre->tls);
        tls_free(cre->tls);
@@ -1784,7 +1815,6 @@ relay_reset_event(struct ctl_relay_event
                close(cre->s);
                rv = 1;
        }
-       cre->state = STATE_DONE;
        cre->bev = NULL;
        cre->tls = NULL;
        cre->tls_cfg = NULL;
Index: usr.sbin/relayd/relay_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v
retrieving revision 1.72
diff -u -p -u -p -r1.72 relay_http.c
--- usr.sbin/relayd/relay_http.c        4 Mar 2019 21:25:03 -0000       1.72
+++ usr.sbin/relayd/relay_http.c        8 May 2019 14:26:40 -0000
@@ -71,9 +71,11 @@ int           relay_httpurl_test(struct ctl_rela
                    struct relay_rule *, struct kvlist *);
 int             relay_httpcookie_test(struct ctl_relay_event *,
                    struct relay_rule *, struct kvlist *);
-int             relay_apply_actions(struct ctl_relay_event *, struct kvlist *);
+int             relay_apply_actions(struct ctl_relay_event *, struct kvlist *,
+                   struct relay_table *);
 int             relay_match_actions(struct ctl_relay_event *,
-                   struct relay_rule *, struct kvlist *, struct kvlist *);
+                   struct relay_rule *, struct kvlist *, struct kvlist *,
+                   struct relay_table **);
 void            relay_httpdesc_free(struct http_descriptor *);
 
 static struct relayd   *env = NULL;
@@ -1509,7 +1511,7 @@ relay_httpcookie_test(struct ctl_relay_e
 
 int
 relay_match_actions(struct ctl_relay_event *cre, struct relay_rule *rule,
-    struct kvlist *matches, struct kvlist *actions)
+    struct kvlist *matches, struct kvlist *actions, struct relay_table **tbl)
 {
        struct rsession         *con = cre->con;
        struct kv               *kv, *tmp;
@@ -1518,11 +1520,9 @@ relay_match_actions(struct ctl_relay_eve
         * Apply the following options instantly (action per match).
         */
        if (rule->rule_table != NULL)
-               con->se_table = rule->rule_table;
-
+               *tbl = rule->rule_table;
        if (rule->rule_tag != 0)
                con->se_tag = rule->rule_tag == -1 ? 0 : rule->rule_tag;
-
        if (rule->rule_label != 0)
                con->se_label = rule->rule_label == -1 ? 0 : rule->rule_label;
 
@@ -1546,7 +1546,8 @@ relay_match_actions(struct ctl_relay_eve
 }
 
 int
-relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions)
+relay_apply_actions(struct ctl_relay_event *cre, struct kvlist *actions,
+    struct relay_table *tbl)
 {
        struct rsession         *con = cre->con;
        struct http_descriptor  *desc = cre->desc;
@@ -1735,12 +1736,22 @@ relay_apply_actions(struct ctl_relay_eve
        }
 
        /*
+        * Change the backend if the forward table has been changed.
+        * This only works in the request direction.
+        */
+       if (cre->dir == RELAY_DIR_REQUEST && con->se_table != tbl) {
+               relay_reset_event(con, &con->se_out);
+               con->se_table = tbl;
+               con->se_haslog = 1;
+       }
+
+       /*
         * log tag for request and response, request method
         * and end of request marker ","
         */
        if ((con->se_log != NULL) &&
            ((meth = relay_httpmethod_byid(desc->http_method)) != NULL) &&
-           (asprintf(&msg, " %s",meth) >= 0))
+           (asprintf(&msg, " %s", meth) != -1))
                evbuffer_add(con->se_log, msg, strlen(msg));
        free(msg);
        relay_log(con, cre->dir == RELAY_DIR_REQUEST ? "" : ";");
@@ -1770,6 +1781,7 @@ relay_test(struct protocol *proto, struc
        struct rsession         *con;
        struct http_descriptor  *desc = cre->desc;
        struct relay_rule       *r = NULL, *rule = NULL;
+       struct relay_table      *tbl = NULL;
        u_int                    cnt = 0;
        u_int                    action = RES_PASS;
        struct kvlist            actions, matches;
@@ -1820,7 +1832,7 @@ relay_test(struct protocol *proto, struc
 
                        if (r->rule_action == RULE_ACTION_MATCH) {
                                if (relay_match_actions(cre, r, &matches,
-                                   &actions) != 0) {
+                                   &actions, &tbl) != 0) {
                                        /* Something bad happened, drop */
                                        action = RES_DROP;
                                        break;
@@ -1854,13 +1866,13 @@ relay_test(struct protocol *proto, struc
                }
        }
 
-       if (rule != NULL && relay_match_actions(cre, rule, NULL, &actions)
+       if (rule != NULL && relay_match_actions(cre, rule, NULL, &actions, &tbl)
            != 0) {
                /* Something bad happened, drop */
                action = RES_DROP;
        }
 
-       if (relay_apply_actions(cre, &actions) != 0) {
+       if (relay_apply_actions(cre, &actions, tbl) != 0) {
                /* Something bad happened, drop */
                action = RES_DROP;
        }
Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.252
diff -u -p -u -p -r1.252 relayd.h
--- usr.sbin/relayd/relayd.h    4 Mar 2019 21:25:03 -0000       1.252
+++ usr.sbin/relayd/relayd.h    8 May 2019 14:26:40 -0000
@@ -193,6 +193,7 @@ enum relay_state {
        STATE_PENDING,
        STATE_PRECONNECT,
        STATE_CONNECTED,
+       STATE_CLOSED,
        STATE_DONE
 };
 
@@ -555,6 +556,7 @@ struct rsession {
        void                            *se_priv;
        SIPHASH_CTX                      se_siphashctx;
        struct relay_table              *se_table;
+       struct relay_table              *se_table0;
        struct event                     se_ev;
        struct timeval                   se_timeout;
        struct timeval                   se_tv_start;
@@ -1134,6 +1136,9 @@ int        cmdline_symset(char *);
 const char *host_error(enum host_error);
 const char *host_status(enum host_status);
 const char *table_check(enum table_check);
+#ifdef DEBUG
+const char *relay_state(enum relay_state);
+#endif
 const char *print_availability(u_long, u_long);
 const char *print_host(struct sockaddr_storage *, char *, size_t);
 const char *print_time(struct timeval *, struct timeval *, char *, size_t);
@@ -1178,7 +1183,7 @@ int        relay_session_cmp(struct rsession *
 char   *relay_load_fd(int, off_t *);
 int     relay_load_certfiles(struct relay *);
 void    relay_close(struct rsession *, const char *, int);
-int     relay_reset_event(struct ctl_relay_event *);
+int     relay_reset_event(struct rsession *, struct ctl_relay_event *);
 void    relay_natlook(int, short, void *);
 void    relay_session(struct rsession *);
 int     relay_from_table(struct rsession *);
Index: usr.sbin/relayd/util.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/util.c,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 util.c
--- usr.sbin/relayd/util.c      21 Nov 2015 12:37:42 -0000      1.1
+++ usr.sbin/relayd/util.c      8 May 2019 14:26:41 -0000
@@ -177,6 +177,29 @@ table_check(enum table_check check)
        return ("invalid");
 }
 
+#ifdef DEBUG
+const char *
+relay_state(enum relay_state state)
+{
+       switch (state) {
+       case STATE_INIT:
+               return ("init");
+       case STATE_PENDING:
+               return ("pending");
+       case STATE_PRECONNECT:
+               return ("preconnect");
+       case STATE_CONNECTED:
+               return ("connected");
+       case STATE_CLOSED:
+               return ("closed");
+       case STATE_DONE:
+               return ("done");
+       };
+       /* NOTREACHED */
+       return ("invalid");
+}
+#endif
+
 const char *
 print_availability(u_long cnt, u_long up)
 {

Reply via email to