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;
}