mirror of https://github.com/omar-polo/gmid.git
added ``block return'' and ``strip'' options
This commit is contained in:
parent
daac4a9452
commit
6abda252e9
|
@ -1,3 +1,7 @@
|
|||
2021-02-06 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (locopt): added ``block return'' and ``strip'' options
|
||||
|
||||
2021-02-05 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* iri.c (parse_query): don't %-decode the query part. This affects the value of QUERY_STRING for CGI scripts too, since that must be %-encoded and we're currently shipping it decoded.
|
||||
|
|
38
gmid.1
38
gmid.1
|
@ -231,6 +231,44 @@ A
|
|||
section may include most of the server configuration rules
|
||||
except
|
||||
.Ic cert , Ic key , Ic root , Ic location No and Ic cgi .
|
||||
.It Ic block Op Ic return Ar code Op Ar meta
|
||||
Send a reply and close the connection;
|
||||
.Ar code
|
||||
is 40
|
||||
and
|
||||
.Ar meta
|
||||
is
|
||||
.Dq temporary failure
|
||||
by default.
|
||||
If
|
||||
.Ar code
|
||||
is in the 3x range, then
|
||||
.Ar meta
|
||||
must be provided.
|
||||
Inside
|
||||
.Ar meta ,
|
||||
the following special sequences are replaced:
|
||||
.Bl -tag -compact
|
||||
.It %%
|
||||
is replaced with a single
|
||||
.Sq % .
|
||||
.It %p
|
||||
is replaced with the request path.
|
||||
.It %q
|
||||
is replaced with the query string of the request.
|
||||
.It %P
|
||||
is replaced with the server port.
|
||||
.It %N
|
||||
is replaced with the server name.
|
||||
.El
|
||||
.It Ic strip Ar number
|
||||
Strip
|
||||
.Ar number
|
||||
components from the beginning of the path.
|
||||
It's only considered for the
|
||||
.Ar meta
|
||||
parameter in the scope of a matching
|
||||
.Ic block return .
|
||||
.El
|
||||
.Sh CGI
|
||||
When a request for an executable file matches the
|
||||
|
|
1
gmid.c
1
gmid.c
|
@ -439,6 +439,7 @@ free_config(void)
|
|||
free((char*)l->lang);
|
||||
free((char*)l->default_mime);
|
||||
free((char*)l->index);
|
||||
free((char*)l->block_fmt);
|
||||
}
|
||||
}
|
||||
memset(hosts, 0, sizeof(hosts));
|
||||
|
|
7
gmid.h
7
gmid.h
|
@ -69,6 +69,9 @@ struct location {
|
|||
const char *default_mime;
|
||||
const char *index;
|
||||
int auto_index; /* 0 auto, -1 off, 1 on */
|
||||
int block_code;
|
||||
const char *block_fmt;
|
||||
int strip;
|
||||
};
|
||||
|
||||
struct vhost {
|
||||
|
@ -208,7 +211,7 @@ extern int yylineno;
|
|||
extern int yyparse(void);
|
||||
extern int yylex(void);
|
||||
|
||||
void yyerror(const char*);
|
||||
void yyerror(const char*, ...);
|
||||
int parse_portno(const char*);
|
||||
void parse_conf(const char*);
|
||||
|
||||
|
@ -223,6 +226,8 @@ const char *vhost_lang(struct vhost*, const char*);
|
|||
const char *vhost_default_mime(struct vhost*, const char*);
|
||||
const char *vhost_index(struct vhost*, const char*);
|
||||
int vhost_auto_index(struct vhost*, const char*);
|
||||
int vhost_block_return(struct vhost*, const char*, int*, const char**);
|
||||
int vhost_strip(struct vhost*, const char*);
|
||||
void mark_nonblock(int);
|
||||
void loop(struct tls*, int, int);
|
||||
|
||||
|
|
3
lex.l
3
lex.l
|
@ -69,6 +69,9 @@ cgi return TCGI;
|
|||
lang return TLANG;
|
||||
index return TINDEX;
|
||||
auto return TAUTO;
|
||||
strip return TSTRIP;
|
||||
block return TBLOCK;
|
||||
return return TRETURN;
|
||||
|
||||
[{}] return *yytext;
|
||||
|
||||
|
|
77
parse.y
77
parse.y
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -36,10 +37,13 @@ size_t iloc;
|
|||
int goterror = 0;
|
||||
const char *config_path;
|
||||
|
||||
void yyerror(const char*);
|
||||
void yyerror(const char*, ...);
|
||||
int parse_portno(const char*);
|
||||
void parse_conf(const char*);
|
||||
char *ensure_absolute_path(char*);
|
||||
int check_block_code(int);
|
||||
char *check_block_fmt(char*);
|
||||
int check_strip_no(int);
|
||||
|
||||
%}
|
||||
|
||||
|
@ -54,6 +58,7 @@ char *ensure_absolute_path(char*);
|
|||
%token TIPV6 TPORT TPROTOCOLS TMIME TDEFAULT TTYPE
|
||||
%token TCHROOT TUSER TSERVER
|
||||
%token TLOCATION TCERT TKEY TROOT TCGI TLANG TINDEX TAUTO
|
||||
%token TSTRIP TBLOCK TRETURN
|
||||
%token TERR
|
||||
|
||||
%token <str> TSTRING
|
||||
|
@ -156,15 +161,42 @@ locopt : TDEFAULT TTYPE TSTRING {
|
|||
loc->index = $2;
|
||||
}
|
||||
| TAUTO TINDEX TBOOL { loc->auto_index = $3 ? 1 : -1; }
|
||||
| TBLOCK TRETURN TNUM TSTRING {
|
||||
if (loc->block_fmt != NULL)
|
||||
yyerror("`block' rule specified more than once");
|
||||
loc->block_fmt = check_block_fmt($4);
|
||||
loc->block_code = check_block_code($3);
|
||||
}
|
||||
| TBLOCK TRETURN TNUM {
|
||||
if (loc->block_fmt != NULL)
|
||||
yyerror("`block' rule specified more than once");
|
||||
loc->block_fmt = xstrdup("temporary failure");
|
||||
loc->block_code = check_block_code($3);
|
||||
if ($3 >= 30 && $3 < 40)
|
||||
yyerror("missing `meta' for block return %d", $3);
|
||||
}
|
||||
| TBLOCK {
|
||||
if (loc->block_fmt != NULL)
|
||||
yyerror("`block' rule specified more than once");
|
||||
loc->block_fmt = xstrdup("temporary failure");
|
||||
loc->block_code = 40;
|
||||
}
|
||||
| TSTRIP TNUM { loc->strip = check_strip_no($2); }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
void
|
||||
yyerror(const char *msg)
|
||||
yyerror(const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
goterror = 1;
|
||||
fprintf(stderr, "%s:%d: %s\n", config_path, yylineno, msg);
|
||||
|
||||
va_start(ap, msg);
|
||||
fprintf(stderr, "%s:%d: ", config_path, yylineno);
|
||||
vfprintf(stderr, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -204,3 +236,42 @@ ensure_absolute_path(char *path)
|
|||
yyerror("not an absolute path");
|
||||
return path;
|
||||
}
|
||||
|
||||
int
|
||||
check_block_code(int n)
|
||||
{
|
||||
if (n < 10 || n >= 70 || (n >= 20 && n <= 29))
|
||||
yyerror("invalid block code %d", n);
|
||||
return n;
|
||||
}
|
||||
|
||||
char *
|
||||
check_block_fmt(char *fmt)
|
||||
{
|
||||
char *s;
|
||||
|
||||
for (s = fmt; *s; ++s) {
|
||||
if (*s != '%')
|
||||
continue;
|
||||
switch (*++s) {
|
||||
case '%':
|
||||
case 'p':
|
||||
case 'q':
|
||||
case 'P':
|
||||
case 'N':
|
||||
break;
|
||||
default:
|
||||
yyerror("invalid format specifier %%%c", *s);
|
||||
}
|
||||
}
|
||||
|
||||
return fmt;
|
||||
}
|
||||
|
||||
int
|
||||
check_strip_no(int n)
|
||||
{
|
||||
if (n <= 0)
|
||||
yyerror("invalid strip number %d", n);
|
||||
return n;
|
||||
}
|
||||
|
|
|
@ -220,3 +220,41 @@ echo OK GET /dir/ with auto index on
|
|||
|
||||
check "should be running"
|
||||
quit
|
||||
|
||||
# test block return and strip
|
||||
|
||||
config '' 'location "*" { block }'
|
||||
checkconf
|
||||
run
|
||||
|
||||
eq "$(head /)" "40 temporary failure" "Unexpected head for /"
|
||||
eq "$(get /)" "" "Unexpected body for /"
|
||||
echo OK GET / with block
|
||||
|
||||
eq "$(head /nonexists)" "40 temporary failure" "Unexpected head for /nonexists"
|
||||
eq "$(get /nonexists)" "" "Unexpected body for /nonexists"
|
||||
echo OK GET /nonexists with block
|
||||
|
||||
check "should be running"
|
||||
quit
|
||||
|
||||
config '' '
|
||||
location "/dir" {
|
||||
strip 1
|
||||
block return 40 "%% %p %q %P %N test"
|
||||
}
|
||||
location "*" {
|
||||
strip 99
|
||||
block return 40 "%% %p %q %P %N test"
|
||||
}'
|
||||
checkconf
|
||||
run
|
||||
|
||||
eq "$(head /dir/foo.gmi)" "40 % /foo.gmi 10965 localhost test"
|
||||
echo OK GET /dir/foo.gmi with strip and block
|
||||
|
||||
eq "$(head /bigfile)" "40 % 10965 localhost test"
|
||||
echo OK GET /bigfile with strip and block
|
||||
|
||||
check "should be running"
|
||||
quit
|
||||
|
|
112
server.c
112
server.c
|
@ -35,6 +35,7 @@ static void open_file(struct pollfd*, struct client*);
|
|||
static void load_file(struct pollfd*, struct client*);
|
||||
static void check_for_cgi(struct pollfd*, struct client*);
|
||||
static void handle_handshake(struct pollfd*, struct client*);
|
||||
static int apply_block_return(struct pollfd*, struct client*);
|
||||
static void handle_open_conn(struct pollfd*, struct client*);
|
||||
static void start_reply(struct pollfd*, struct client*, int, const char*);
|
||||
static void handle_start_reply(struct pollfd*, struct client*);
|
||||
|
@ -131,6 +132,47 @@ vhost_auto_index(struct vhost *v, const char *path)
|
|||
return v->locations[0].auto_index == 1;
|
||||
}
|
||||
|
||||
int
|
||||
vhost_block_return(struct vhost *v, const char *path, int *code, const char **fmt)
|
||||
{
|
||||
struct location *loc;
|
||||
|
||||
if (v == NULL || path == NULL)
|
||||
return 0;
|
||||
|
||||
for (loc = &v->locations[1]; loc->match != NULL; ++loc) {
|
||||
if (!fnmatch(loc->match, path, 0)) {
|
||||
if (loc->block_code != 0) {
|
||||
*code = loc->block_code;
|
||||
*fmt = loc->block_fmt;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*code = v->locations[0].block_code;
|
||||
*fmt = v->locations[0].block_fmt;
|
||||
return v->locations[0].block_code != 0;
|
||||
}
|
||||
|
||||
int
|
||||
vhost_strip(struct vhost *v, const char *path)
|
||||
{
|
||||
struct location *loc;
|
||||
|
||||
if (v == NULL || path == NULL)
|
||||
return 0;
|
||||
|
||||
for (loc = &v->locations[1]; loc->match != NULL; ++loc) {
|
||||
if (!fnmatch(loc->match, path, 0)) {
|
||||
if (loc->strip != 0)
|
||||
return loc->strip;
|
||||
}
|
||||
}
|
||||
|
||||
return v->locations[0].strip;
|
||||
}
|
||||
|
||||
static int
|
||||
check_path(struct client *c, const char *path, int *fd)
|
||||
{
|
||||
|
@ -333,6 +375,73 @@ err:
|
|||
start_reply(fds, c, BAD_REQUEST, "Wrong/malformed host or missing SNI");
|
||||
}
|
||||
|
||||
/* 1 if a matching `block return' (and apply it), 0 otherwise */
|
||||
static int
|
||||
apply_block_return(struct pollfd *fds, struct client *c)
|
||||
{
|
||||
char *t, *path, buf[32];
|
||||
const char *fmt;
|
||||
int strip, code;
|
||||
size_t i;
|
||||
|
||||
if (!vhost_block_return(c->host, c->iri.path, &code, &fmt))
|
||||
return 0;
|
||||
|
||||
strip = vhost_strip(c->host, c->iri.path);
|
||||
path = c->iri.path;
|
||||
while (strip > 0) {
|
||||
if ((t = strchr(path, '/')) == NULL) {
|
||||
path = strchr(path, '\0');
|
||||
break;
|
||||
}
|
||||
path = t;
|
||||
strip--;
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
for (i = 0; *fmt; ++fmt) {
|
||||
if (i == sizeof(buf)-1 || *fmt == '%') {
|
||||
strlcat(c->sbuf, buf, sizeof(c->sbuf));
|
||||
memset(buf, 0, sizeof(buf));
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (*fmt != '%') {
|
||||
buf[i++] = *fmt;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (*++fmt) {
|
||||
case '%':
|
||||
strlcat(c->sbuf, "%", sizeof(c->sbuf));
|
||||
break;
|
||||
case 'p':
|
||||
strlcat(c->sbuf, path, sizeof(c->sbuf));
|
||||
break;
|
||||
case 'q':
|
||||
strlcat(c->sbuf, c->iri.query, sizeof(c->sbuf));
|
||||
break;
|
||||
case 'P':
|
||||
snprintf(buf, sizeof(buf), "%d", conf.port);
|
||||
strlcat(c->sbuf, buf, sizeof(c->sbuf));
|
||||
memset(buf, 0, sizeof(buf));
|
||||
break;
|
||||
case 'N':
|
||||
strlcat(c->sbuf, c->domain, sizeof(c->sbuf));
|
||||
break;
|
||||
default:
|
||||
fatal("%s: unknown fmt specifier %c",
|
||||
__func__, *fmt);
|
||||
}
|
||||
}
|
||||
|
||||
if (i != 0)
|
||||
strlcat(c->sbuf, buf, sizeof(c->sbuf));
|
||||
|
||||
start_reply(fds, c, code, c->sbuf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_open_conn(struct pollfd *fds, struct client *c)
|
||||
{
|
||||
|
@ -372,7 +481,8 @@ handle_open_conn(struct pollfd *fds, struct client *c)
|
|||
return;
|
||||
}
|
||||
|
||||
open_file(fds, c);
|
||||
if (!apply_block_return(fds, c))
|
||||
open_file(fds, c);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
Loading…
Reference in New Issue