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(¶m, "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(¶m, "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(¶m, "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(¶m, "REQUEST_URI", str, clt); - free(str); - if (ret == -1) { + if (fcgi_add_param(¶m, "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; }