Hi,

There have been some patches for supporting rewriting in httpd. Since this
is a feature I need to be able to switch over to stock httpd, I thought I
would chime in with a patch as well. Maybe (or maybe not) this can help
move things a step further to get the feature implemented.

Some differences from other patches I've seen are:

* Preserves REQUEST_URI with the original requested URI, not the rewritten
uri. This is the behavior of most other http servers afaik.
* If there is a query string in the original request, it's preserved in the
rewritten uri.
* I decided to not use return_uri, but rather created a new variable called
rewrite_uri. From what I can see return_uri and rewrite_uri are mutually
exclusive, but I'm not sure this will always be true in the future. Also it
didn't feel intuitive when the variable is called return since it's a
rewrite. However, this made the patch much bigger as well.

The patch is against -current, but I've only been able to test it on 6.1
system. So if anyone is able to run it on a -current system that would be
good. I don't see why it should work there as well, though.

Feedback? Opinions? Thoughts?

//Ibo
Index: config.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/config.c,v
retrieving revision 1.53
diff -u -p -r1.53 config.c
--- config.c    19 Jul 2017 17:36:25 -0000      1.53
+++ config.c    27 Jul 2017 20:52:41 -0000
@@ -193,6 +193,10 @@ config_setserver(struct httpd *env, stru
                        iov[c].iov_base = srv->srv_conf.return_uri;
                        iov[c++].iov_len = srv->srv_conf.return_uri_len;
                }
+               if (srv->srv_conf.rewrite_uri_len != 0) {
+                       iov[c].iov_base = srv->srv_conf.rewrite_uri;
+                       iov[c++].iov_len = srv->srv_conf.rewrite_uri_len;
+               }
 
                if (id == PROC_SERVER &&
                    (srv->srv_conf.flags & SRVFLAG_LOCATION) == 0) {
@@ -378,6 +382,13 @@ config_getserver_config(struct httpd *en
                s += srv_conf->return_uri_len;
        }
 
+       if (srv_conf->rewrite_uri_len != 0) {
+               if ((srv_conf->rewrite_uri = get_data(p + s,
+                   srv_conf->rewrite_uri_len)) == NULL)
+                       goto fail;
+               s += srv_conf->rewrite_uri_len;
+       }
+
        if (srv_conf->flags & SRVFLAG_LOCATION) {
                /* Inherit configuration from the parent */
                f = SRVFLAG_INDEX|SRVFLAG_NO_INDEX;
@@ -569,6 +580,13 @@ config_getserver(struct httpd *env, stru
                    srv->srv_conf.return_uri_len)) == NULL)
                        goto fail;
                s += srv->srv_conf.return_uri_len;
+       }
+
+       if (srv->srv_conf.rewrite_uri_len != 0) {
+               if ((srv->srv_conf.rewrite_uri = get_data(p + s,
+                   srv->srv_conf.rewrite_uri_len)) == NULL)
+                       goto fail;
+               s += srv->srv_conf.rewrite_uri_len;
        }
 
        return (0);
Index: http.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/http.h,v
retrieving revision 1.14
diff -u -p -r1.14 http.h
--- http.h      1 Aug 2016 21:15:30 -0000       1.14
+++ http.h      27 Jul 2017 20:52:41 -0000
@@ -242,6 +242,7 @@ struct http_descriptor {
        int                      http_chunked;
        char                    *http_version;
        unsigned int             http_status;
+       char                    *http_orig_request_uri;
 
        /* Rewritten path remains NULL if not used */
        char                    *http_path_alias;
Index: httpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.conf.5,v
retrieving revision 1.82
diff -u -p -r1.82 httpd.conf.5
--- httpd.conf.5        9 Apr 2017 09:13:28 -0000       1.82
+++ httpd.conf.5        27 Jul 2017 20:52:41 -0000
@@ -396,6 +396,8 @@ The pattern may contain captures that ca
 .Ar uri
 of an enclosed
 .Ic block return
+or
+.Ic pass rewrite
 option.
 .It Oo Ic no Oc Ic log Op Ar option
 Set the specified logging options.
@@ -454,6 +456,10 @@ instead of the log files.
 Disable any previous
 .Ic block
 in a location.
+.It Ic pass Ic rewrite Ar uri
+Rewrites the requested URI before handling the request. See
+.Ic block rewrite
+for information about available macros.
 .It Ic root Ar option
 Configure the document root and options for the request path.
 Valid options are:
Index: httpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/httpd.h,v
retrieving revision 1.133
diff -u -p -r1.133 httpd.h
--- httpd.h     19 Jul 2017 17:36:25 -0000      1.133
+++ httpd.h     27 Jul 2017 20:52:41 -0000
@@ -394,13 +394,14 @@ SPLAY_HEAD(client_tree, client);
 #define SRVFLAG_SERVER_MATCH   0x00200000
 #define SRVFLAG_SERVER_HSTS    0x00400000
 #define SRVFLAG_DEFAULT_TYPE   0x00800000
+#define SRVFLAG_REWRITE                0x01000000
 
 #define SRVFLAG_BITS                                                   \
        "\10\01INDEX\02NO_INDEX\03AUTO_INDEX\04NO_AUTO_INDEX"           \
        "\05ROOT\06LOCATION\07FCGI\10NO_FCGI\11LOG\12NO_LOG\13SOCKET"   \
        "\14SYSLOG\15NO_SYSLOG\16TLS\17ACCESS_LOG\20ERROR_LOG"          \
        "\21AUTH\22NO_AUTH\23BLOCK\24NO_BLOCK\25LOCATION_MATCH"         \
-       "\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE"
+       "\26SERVER_MATCH\27SERVER_HSTS\30DEFAULT_TYPE\31REWRITE"
 
 #define TCPFLAG_NODELAY                0x01
 #define TCPFLAG_NNODELAY       0x02
@@ -511,6 +512,9 @@ struct server_config {
        int                      return_code;
        char                    *return_uri;
        off_t                    return_uri_len;
+
+       char                    *rewrite_uri;
+       off_t                    rewrite_uri_len;
 
        int                      hsts_max_age;
        uint8_t                  hsts_flags;
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.90
diff -u -p -r1.90 parse.y
--- parse.y     25 Mar 2017 17:25:34 -0000      1.90
+++ parse.y     27 Jul 2017 20:52:42 -0000
@@ -134,7 +134,7 @@ typedef struct {
 %token LISTEN LOCATION LOG LOGDIR MATCH MAXIMUM NO NODELAY OCSP ON PORT PREFORK
 %token PROTOCOLS REQUESTS ROOT SACK SERVER SOCKET STRIP STYLE SYSLOG TCP TICKET
 %token TIMEOUT TLS TYPE TYPES HSTS MAXAGE SUBDOMAINS DEFAULT PRELOAD REQUEST
-%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS
+%token ERROR INCLUDE AUTHENTICATE WITH BLOCK DROP RETURN PASS REWRITE
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.port>        port
@@ -994,6 +994,13 @@ filter             : block RETURN NUMBER optstring 
                        /* Forbidden */
                        srv_conf->return_code = 403;
                }
+               | PASS REWRITE STRING           {
+                       srv_conf->flags &= ~SRVFLAG_BLOCK;
+                       srv_conf->flags |= SRVFLAG_NO_BLOCK;
+                       srv_conf->flags |= SRVFLAG_REWRITE;
+                       srv_conf->rewrite_uri = $3;
+                       srv_conf->rewrite_uri_len = strlen($3) + 1;
+               }
                | PASS                          {
                        srv_conf->flags &= ~SRVFLAG_BLOCK;
                        srv_conf->flags |= SRVFLAG_NO_BLOCK;
@@ -1255,6 +1262,7 @@ lookup(char *s)
                { "request",            REQUEST },
                { "requests",           REQUESTS },
                { "return",             RETURN },
+               { "rewrite",            REWRITE },
                { "root",               ROOT },
                { "sack",               SACK },
                { "server",             SERVER },
@@ -2058,6 +2066,11 @@ server_inherit(struct server *src, struc
        if (src->srv_conf.return_uri != NULL &&
            (dst->srv_conf.return_uri =
            strdup(src->srv_conf.return_uri)) == NULL)
+               fatal("out of memory");
+
+       if (src->srv_conf.rewrite_uri != NULL &&
+           (dst->srv_conf.rewrite_uri =
+           strdup(src->srv_conf.rewrite_uri)) == NULL)
                fatal("out of memory");
 
        dst->srv_conf.id = ++last_server_id;
Index: server.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server.c,v
retrieving revision 1.110
diff -u -p -r1.110 server.c
--- server.c    19 Jul 2017 17:36:25 -0000      1.110
+++ server.c    27 Jul 2017 20:52:42 -0000
@@ -412,6 +412,7 @@ void
 serverconfig_free(struct server_config *srv_conf)
 {
        free(srv_conf->return_uri);
+       free(srv_conf->rewrite_uri);
        free(srv_conf->tls_cert_file);
        free(srv_conf->tls_key_file);
        free(srv_conf->tls_ocsp_staple_file);
@@ -425,6 +426,7 @@ serverconfig_reset(struct server_config 
 {
        srv_conf->auth = NULL;
        srv_conf->return_uri = NULL;
+       srv_conf->rewrite_uri = NULL;
        srv_conf->tls_cert = NULL;
        srv_conf->tls_cert_file = NULL;
        srv_conf->tls_key = NULL;
Index: server_fcgi.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_fcgi.c,v
retrieving revision 1.74
diff -u -p -r1.74 server_fcgi.c
--- server_fcgi.c       21 Jan 2017 11:32:04 -0000      1.74
+++ server_fcgi.c       27 Jul 2017 20:52:42 -0000
@@ -306,21 +306,29 @@ server_fcgi(struct httpd *env, struct cl
                goto fail;
        }
 
-       if (!desc->http_query) {
-               if (fcgi_add_param(&param, "REQUEST_URI", desc->http_path,
-                   clt) == -1) {
-                       errstr = "failed to encode param";
-                       goto fail;
+       if (!desc->http_orig_request_uri) {
+               if (!desc->http_query) {
+                       if (fcgi_add_param(&param, "REQUEST_URI",
+                           desc->http_path, clt) == -1) {
+                               errstr = "failed to encode param";
+                               goto fail;
+                       }
+               } else {
+                       if (asprintf(&str, "%s?%s", desc->http_path,
+                       desc->http_query) == -1) {
+                               errstr = "failed to encode param";
+                               goto fail;
+                       }
+                       ret = fcgi_add_param(&param, "REQUEST_URI", str, clt);
+                       free(str);
+                       if (ret == -1) {
+                               errstr = "failed to encode param";
+                               goto fail;
+                       }
                }
        } else {
-               if (asprintf(&str, "%s?%s", desc->http_path,
-                   desc->http_query) == -1) {
-                       errstr = "failed to encode param";
-                       goto fail;
-               }
-               ret = fcgi_add_param(&param, "REQUEST_URI", str, clt);
-               free(str);
-               if (ret == -1) {
+               if (fcgi_add_param(&param, "REQUEST_URI",
+                   desc->http_orig_request_uri, clt) == -1) {
                        errstr = "failed to encode param";
                        goto fail;
                }
Index: server_http.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server_http.c,v
retrieving revision 1.117
diff -u -p -r1.117 server_http.c
--- server_http.c       15 May 2017 10:40:47 -0000      1.117
+++ server_http.c       27 Jul 2017 20:52:42 -0000
@@ -48,6 +48,7 @@ int            server_http_authenticate(struct se
                    struct client *);
 char           *server_expand_http(struct client *, const char *,
                    char *, size_t);
+int             server_rewrite(struct client *);
 
 static struct http_method       http_methods[] = HTTP_METHODS;
 static struct http_error        http_errors[] = HTTP_ERRORS;
@@ -108,6 +109,8 @@ server_httpdesc_free(struct http_descrip
        desc->http_version = NULL;
        free(desc->http_host);
        desc->http_host = NULL;
+       free(desc->http_orig_request_uri);
+       desc->http_orig_request_uri = NULL;
 
        kv_purge(&desc->http_headers);
        desc->http_lastheader = NULL;
@@ -1292,6 +1295,14 @@ server_response(struct httpd *httpd, str
            server_http_authenticate(srv_conf, clt) == -1) {
                server_abort_http(clt, 401, srv_conf->auth_realm);
                return (-1);
+       } else if (srv_conf->flags & SRVFLAG_REWRITE) {
+               ret = server_rewrite(clt);
+               if (ret == -1)
+                       return (-1);
+               else if (ret == 0) {
+                       srv_conf = server_getlocation(clt, desc->http_path);
+                       return (server_file(httpd, clt));
+               }
        } else
                return (server_file(httpd, clt));
  fail:
@@ -1721,4 +1732,69 @@ done:
        free(agent_v);
 
        return (ret);
+}
+
+int server_rewrite(struct client *clt)
+{
+       char buf[IBUF_READ_SIZE];
+       char *query, *str;
+
+       struct http_descriptor  *desc = clt->clt_descreq;
+       struct server_config    *srv_conf = clt->clt_srv_conf;
+
+       memset(buf, 0, sizeof(buf));
+       if (server_expand_http(clt, srv_conf->rewrite_uri, buf,
+           sizeof(buf)) == NULL) {
+               server_abort_http(clt, 500, strerror(errno));
+               return (-1);
+       }
+
+       DPRINTF("%s: Rewriting %s to %s", __func__, desc->http_path, buf);
+
+       /* Save original REQUEST_URI */
+       if (!desc->http_query) {
+               desc->http_orig_request_uri = strdup(desc->http_path);
+       } else {
+               if (asprintf(&str, "%s?%s", desc->http_path,
+                   desc->http_query) == -1) {
+                       DPRINTF("%s: asprintf failed", __func__);
+                       server_abort_http(clt, 500, strerror(errno));
+                       return (-1);
+               }
+               desc->http_orig_request_uri = strdup(str);
+               free(str);
+       }
+       if (desc->http_orig_request_uri == NULL) {
+               server_abort_http(clt, 500, strerror(errno));
+               return (-1);
+       }
+
+       if (desc->http_path != NULL) {
+               free(desc->http_path);
+               desc->http_path = NULL;
+       }
+       desc->http_path = strdup(buf);
+       if (desc->http_path == NULL) {
+               server_abort_http(clt, 500, strerror(errno));
+               return (-1);
+       }
+
+       query = strchr(desc->http_path, '?');
+       if (query != NULL) {
+               memset(buf, 0, sizeof(buf));
+               *query++ = '\0';
+               if (!desc->http_query) {
+                       strlcpy(buf, query, sizeof(buf));
+               } else {
+                       snprintf(buf, sizeof(buf), "%s&%s",
+                       desc->http_query, query);
+               }
+               desc->http_query = strdup(buf);
+               if (desc->http_query == NULL) {
+                       server_abort_http(clt, 500, strerror(errno));
+                       return (-1);
+               }
+       }
+
+       return 0;
 }

Reply via email to