commit 6abda252e960943a302f7a945b1d008e463ea316 from: Omar Polo date: Sat Feb 06 17:22:37 2021 UTC added ``block return'' and ``strip'' options commit - daac4a945284dd0f8baa3f35234981f7bc0426da commit + 6abda252e960943a302f7a945b1d008e463ea316 blob - ad55d54f5df1052a74d68b89d92e5116af8eea50 blob + a9e08cc67cf25cecb7448cd23442cc0e47c60daa --- ChangeLog +++ ChangeLog @@ -1,3 +1,7 @@ +2021-02-06 Omar Polo + + * parse.y (locopt): added ``block return'' and ``strip'' options + 2021-02-05 Omar Polo * 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. blob - e6249ee72fcecfa99359f878b4e3af0f9bdd5431 blob + 58fa9cb3cf8c9d5473a90fbdd42e5fe025ab5fcc --- gmid.1 +++ gmid.1 @@ -231,7 +231,45 @@ 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 .Ic cgi blob - d3e6737dfb1577ed079eee3efa9e6f6cef3531df blob + 1a1bd59dccdc46a5b479b6f829e38de5cbe506b3 --- gmid.c +++ 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)); blob - e1e4e87734ed4e4a51b346ed5885aefd62520146 blob + e4dd437546bf7bcc3b012601ce32c7d62a31fbfa --- gmid.h +++ 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); blob - a2e4bf9dc4c56d491d54fbf72ed83ca3c8c9b566 blob + 04d902a39bbc4498fe9dc0cc29665682640ff3c1 --- lex.l +++ 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; blob - 4e1bc4b71aae42603160f9476f38c704719ff8f0 blob + 77a3558f73c42d12b25629e5b75f251a8426c231 --- parse.y +++ parse.y @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -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 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; +} blob - b114bebd4202f3f980cce8bc36c3499f5db783d9 blob + 96ac135f8e0e6ce4dcfe4e9df6f56c859e6f4447 --- regress/runtime +++ regress/runtime @@ -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 blob - f5b62e2c4b7176b304e89ad5b3c6f74f41015609 blob + a25f0a7c69f8d9de6e633204678c9a840a9e81b2 --- server.c +++ 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*); @@ -129,6 +130,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 @@ -331,6 +373,73 @@ err: strncpy(c->req, "null", sizeof(c->req)); 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 @@ -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