Hi, I'd like to add an URI stripping option to httpd, which is similar to apache/nginx's alias options:
root [strip number] directory Set the document root of the server. The directory is a pathname within the chroot(2) root directory of httpd. If not specified, it defaults to /htdocs. If the strip option is set, number path components are stripped from the beginning of the request URI before looking up the stripped-down URI at directory. for example: location "/pub/OpenBSD/snapshots/amd64*" { root strip 4 "/OpenBSD.amd64" directory auto index } For serving php: location "/wiki/" { root strip 1 "/dokuwiki" directory index "doku.php" fastcgi socket "/tmp/doku.sock" } location "/wiki/*.php" { root strip 1 "/dokuwiki" fastcgi socket "/tmp/doku.sock" } location "/wiki/lib/*" { root strip 1 "/dokuwiki" directory no index } Comments? OKs? Christopher diff --git httpd.conf.5 httpd.conf.5 index 788d0a9..7776131 100644 --- httpd.conf.5 +++ httpd.conf.5 @@ -229,7 +229,7 @@ Enable or disable logging to .Xr syslog 3 instead of the log files. .El -.It Ic root Ar directory +.It Ic root Oo Ic strip Ar number Oc Ar directory Set the document root of the server. The .Ar directory @@ -239,6 +239,11 @@ root directory of .Nm httpd . If not specified, it defaults to .Pa /htdocs . +If the strip option is set, +.Ar number +path components are stripped from the beginning of the request URI before +looking up the stripped-down URI at +.Ar directory . .It Ic ssl Ar option Set the SSL configuration for the server. These options are only used if SSL has been enabled via the listen directive. diff --git httpd.h httpd.h index 04b1f05..45505a1 100644 --- httpd.h +++ httpd.h @@ -383,6 +383,7 @@ struct server_config { char *ssl_key_file; u_int16_t flags; + u_int8_t strip; u_int8_t tcpflags; int tcpbufsiz; int tcpbacklog; diff --git parse.y parse.y index 44cf90c..70e1cf7 100644 --- parse.y +++ parse.y @@ -128,12 +128,13 @@ typedef struct { %token ACCESS AUTO BACKLOG BODY BUFFER CERTIFICATE CHROOT CIPHERS COMMON %token COMBINED CONNECTION DIRECTORY ERR FCGI INDEX IP KEY LISTEN LOCATION %token LOG MAXIMUM NO NODELAY ON PORT PREFORK REQUEST REQUESTS ROOT SACK -%token SERVER SOCKET SSL STYLE SYSLOG TCP TIMEOUT TYPES +%token SERVER SOCKET SSL STRIP STYLE SYSLOG TCP TIMEOUT TYPES %token ERROR INCLUDE %token <v.string> STRING %token <v.number> NUMBER %type <v.port> port %type <v.number> optssl +%type <v.number> optstrip %type <v.tv> timeout %type <v.string> numberstring @@ -176,6 +177,10 @@ optssl : /*empty*/ { $$ = 0; } | SSL { $$ = 1; } ; +optstrip : /*empty*/ { $$ = 0; } + | STRIP NUMBER { $$ = $2; } + ; + main : PREFORK NUMBER { if (loadcfg) break; @@ -333,16 +338,21 @@ serveroptsl : LISTEN ON STRING optssl port { YYERROR; } } ssl - | ROOT STRING { - if (strlcpy(srv->srv_conf.root, $2, + | ROOT optstrip STRING { + if (strlcpy(srv->srv_conf.root, $3, sizeof(srv->srv_conf.root)) >= sizeof(srv->srv_conf.root)) { yyerror("document root too long"); - free($2); + free($3); YYERROR; } - free($2); + free($3); srv->srv_conf.flags |= SRVFLAG_ROOT; + if ($2 < 0 || $2 > UINT8_MAX) { + yyerror("invalid strip number"); + YYERROR; + } + srv->srv_conf.strip = $2; } | DIRECTORY dirflags | DIRECTORY '{' dirflags_l '}' @@ -848,6 +858,7 @@ lookup(char *s) { "server", SERVER }, { "socket", SOCKET }, { "ssl", SSL }, + { "strip", STRIP }, { "style", STYLE }, { "syslog", SYSLOG }, { "tcp", TCP }, diff --git server_fcgi.c server_fcgi.c index fe97be0..1d591b8 100644 --- server_fcgi.c +++ server_fcgi.c @@ -101,9 +101,12 @@ server_fcgi(struct httpd *env, struct client *clt) struct fcgi_begin_request_body *begin; char hbuf[MAXHOSTNAMELEN]; size_t scriptlen; - int pathlen; + int i, pathlen; int fd = -1, ret; - const char *errstr = NULL; + const char *stripped, *errstr = NULL; + const char *alias = desc->http_path_alias != NULL + ? desc->http_path_alias + : desc->http_path; char *str, *p, *script = NULL; if (srv_conf->socket[0] == ':') { @@ -192,9 +195,14 @@ server_fcgi(struct httpd *env, struct client *clt) h->type = FCGI_PARAMS; h->content_len = param.total_len = 0; - if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, - desc->http_path_alias != NULL ? - desc->http_path_alias : desc->http_path)) == -1) { + /* First strip leading directories. Leading '/' is ignored. */ + stripped = alias; + i = srv_conf->strip; + while (i > 0 && *stripped != '\0') if (*++stripped == '/') i--; + + /* Then prepend root. */ + if ((pathlen = asprintf(&script, "%s%s", srv_conf->root, stripped)) + == -1) { errstr = "failed to get script name"; goto fail; } @@ -214,9 +222,16 @@ server_fcgi(struct httpd *env, struct client *clt) } script[scriptlen] = '\0'; } - - if (fcgi_add_param(¶m, "SCRIPT_NAME", - script + strlen(srv_conf->root), clt) == -1) { + /* + * calculate length of http SCRIPT_NAME: + * add stripped prefix length, remove local root length + */ + scriptlen += (stripped - alias) - strlen(srv_conf->root); + if ((str = strndup(alias, scriptlen)) == NULL) + goto fail; + ret = fcgi_add_param(¶m, "SCRIPT_NAME", str, clt); + free(str); + if (ret == -1) { errstr = "failed to encode param"; goto fail; } diff --git server_file.c server_file.c index e8a487e..987f2b5 100644 --- server_file.c +++ server_file.c @@ -153,17 +153,24 @@ server_file(struct httpd *env, struct client *clt) struct http_descriptor *desc = clt->clt_descreq; struct server_config *srv_conf = clt->clt_srv_conf; char path[MAXPATHLEN]; - const char *errstr = NULL; - int ret = 500; + const char *stripped, *errstr = NULL; + int i, ret = 500; if (srv_conf->flags & SRVFLAG_FCGI) return (server_fcgi(env, clt)); - /* Request path is already canonicalized */ + /* + * Request path is already canonicalized. + * First strip leading directories. Leading '/' is ignored. + */ + stripped = desc->http_path_alias != NULL ? + desc->http_path_alias : desc->http_path; + i = srv_conf->strip; + while (i > 0 && *stripped != '\0') if (*++stripped == '/') i--; + + /* Then prepend root. */ if ((size_t)snprintf(path, sizeof(path), "%s%s", - srv_conf->root, - desc->http_path_alias != NULL ? - desc->http_path_alias : desc->http_path) >= sizeof(path)) { + srv_conf->root, stripped) >= sizeof(path)) { errstr = desc->http_path; goto abort; } @@ -279,7 +286,7 @@ server_file_index(struct httpd *env, struct client *clt, struct stat *st) int code = 500; struct evbuffer *evb = NULL; struct media_type *media; - const char *style; + const char *stripped, *style; struct tm tm; time_t t, dir_mtime; @@ -288,9 +295,17 @@ server_file_index(struct httpd *env, struct client *clt, struct stat *st) goto abort; } - /* Request path is already canonicalized */ + /* + * Request path is already canonicalized. + * First strip leading directories. Leading '/' is ignored. + */ + stripped = desc->http_path; + i = srv_conf->strip; + while (i > 0 && *stripped != '\0') if (*++stripped == '/') i--; + + /* Then prepend root. */ if ((size_t)snprintf(path, sizeof(path), "%s%s", - srv_conf->root, desc->http_path) >= sizeof(path)) + srv_conf->root, stripped) >= sizeof(path)) goto abort; /* Now open the file, should be readable or we have another problem */ -- http://gmerlin.de OpenPGP: http://gmerlin.de/christopher.pub F190 D013 8F01 AA53 E080 3F3C F17F B0A1 D44E 4FEE