commit 7edc455ab4ba2c408558876339ac77a3104d0de2 from: Omar Polo date: Fri Jan 15 19:08:28 2021 UTC move cgi stuff to its own file commit - 5bc3c98ed4e25bc68a72dd6cd6676b25d2cdf9cd commit + 7edc455ab4ba2c408558876339ac77a3104d0de2 blob - c405482cf0cc1dd816e22b43ccec4bd381107688 blob + d2e6c7b1d6c3249de0298437cdc454de069ffff9 --- Makefile +++ Makefile @@ -14,7 +14,7 @@ lex.yy.c: lex.l y.tab.c y.tab.c: parse.y ${YACC} -b y -d parse.y -OBJS = gmid.o iri.o utf8.o lex.yy.o y.tab.o sandbox.o +OBJS = gmid.o iri.o utf8.o lex.yy.o y.tab.o cgi.o sandbox.o gmid: ${OBJS} ${CC} ${OBJS} -o gmid ${LDFLAGS} blob - /dev/null blob + 567517ab7750dac12ff34e749d5f0aa39fb7e3e0 (mode 644) --- /dev/null +++ cgi.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "gmid.h" + +static inline void +safe_setenv(const char *name, const char *val) +{ + if (val == NULL) + val = ""; + setenv(name, val, 1); +} + +/* + * the inverse of this algorithm, i.e. starting from the start of the + * path + strlen(cgi), and checking if each component, should be + * faster. But it's tedious to write. This does the opposite: starts + * from the end and strip one component at a time, until either an + * executable is found or we emptied the path. + */ +int +check_for_cgi(char *path, char *query, struct pollfd *fds, struct client *c) +{ + char *end; + end = strchr(path, '\0'); + + /* NB: assume CGI is enabled and path matches cgi */ + + while (end > path) { + /* go up one level. UNIX paths are simple and POSIX + * dirname, with its ambiguities on if the given path + * is changed or not, gives me headaches. */ + while (*end != '/') + end--; + *end = '\0'; + + switch (check_path(c, path, &c->fd)) { + case FILE_EXECUTABLE: + return start_cgi(path, end+1, query, fds,c); + case FILE_MISSING: + break; + default: + goto err; + } + + *end = '/'; + end--; + } + +err: + if (!start_reply(fds, c, NOT_FOUND, "not found")) + return 0; + goodbye(fds, c); + return 0; +} + +int +start_cgi(const char *spath, const char *relpath, const char *query, + struct pollfd *fds, struct client *c) +{ + pid_t pid; + int p[2]; /* read end, write end */ + + if (pipe(p) == -1) + goto err; + + switch (pid = fork()) { + case -1: + goto err; + + case 0: { /* child */ + char *ex, *requri, *portno; + char addr[NI_MAXHOST]; + char *argv[] = { NULL, NULL, NULL }; + int ec; + + close(p[0]); + if (dup2(p[1], 1) == -1) + goto childerr; + + if (inet_ntop(c->af, &c->addr, addr, sizeof(addr)) == NULL) + goto childerr; + + ec = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr), + addr, sizeof(addr), + NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV); + if (ec != 0) + goto childerr; + + if (asprintf(&portno, "%d", conf.port) == -1) + goto childerr; + + if (asprintf(&ex, "%s/%s", c->host->dir, spath) == -1) + goto childerr; + + if (asprintf(&requri, "%s%s%s", spath, + *relpath == '\0' ? "" : "/", relpath) == -1) + goto childerr; + + argv[0] = argv[1] = ex; + + /* fix the env */ + safe_setenv("GATEWAY_INTERFACE", "CGI/1.1"); + safe_setenv("SERVER_SOFTWARE", "gmid"); + safe_setenv("SERVER_PORT", portno); + /* setenv("SERVER_NAME", "", 1); */ + safe_setenv("SCRIPT_NAME", spath); + safe_setenv("SCRIPT_EXECUTABLE", ex); + safe_setenv("REQUEST_URI", requri); + safe_setenv("REQUEST_RELATIVE", relpath); + safe_setenv("QUERY_STRING", query); + safe_setenv("REMOTE_HOST", addr); + safe_setenv("REMOTE_ADDR", addr); + safe_setenv("DOCUMENT_ROOT", c->host->dir); + + if (tls_peer_cert_provided(c->ctx)) { + safe_setenv("AUTH_TYPE", "Certificate"); + safe_setenv("REMOTE_USER", tls_peer_cert_subject(c->ctx)); + safe_setenv("TLS_CLIENT_ISSUER", tls_peer_cert_issuer(c->ctx)); + safe_setenv("TLS_CLIENT_HASH", tls_peer_cert_hash(c->ctx)); + } + + execvp(ex, argv); + goto childerr; + } + + default: /* parent */ + close(p[1]); + close(c->fd); + c->fd = p[0]; + c->child = pid; + mark_nonblock(c->fd); + c->state = S_SENDING; + handle_cgi(fds, c); + return 0; + } + +err: + if (!start_reply(fds, c, TEMP_FAILURE, "internal server error")) + return 0; + goodbye(fds, c); + return 0; + +childerr: + dprintf(p[1], "%d internal server error\r\n", TEMP_FAILURE); + close(p[1]); + _exit(1); +} + +void +cgi_poll_on_child(struct pollfd *fds, struct client *c) +{ + int fd; + + if (c->waiting_on_child) + return; + c->waiting_on_child = 1; + + fds->events = POLLIN; + + fd = fds->fd; + fds->fd = c->fd; + c->fd = fd; +} + +void +cgi_poll_on_client(struct pollfd *fds, struct client *c) +{ + int fd; + + if (!c->waiting_on_child) + return; + c->waiting_on_child = 0; + + fd = fds->fd; + fds->fd = c->fd; + c->fd = fd; +} + +void +handle_cgi(struct pollfd *fds, struct client *c) +{ + ssize_t r; + + /* ensure c->fd is the child and fds->fd the client */ + cgi_poll_on_client(fds, c); + + while (1) { + if (c->len == 0) { + if ((r = read(c->fd, c->sbuf, sizeof(c->sbuf))) == 0) + goto end; + if (r == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + cgi_poll_on_child(fds, c); + return; + } + goto end; + } + c->len = r; + c->off = 0; + } + + while (c->len > 0) { + switch (r = tls_write(c->ctx, c->sbuf + c->off, c->len)) { + case -1: + goto end; + + case TLS_WANT_POLLOUT: + fds->events = POLLOUT; + return; + + case TLS_WANT_POLLIN: + fds->events = POLLIN; + return; + + default: + c->off += r; + c->len -= r; + break; + } + } + } + +end: + goodbye(fds, c); +} blob - a648ad94118302b1d8f3743ce04f39c507c84b61 blob + 7bd722b609bf1105d2ccb9431e3b7645685f0dd1 --- gmid.c +++ gmid.c @@ -58,14 +58,6 @@ struct etm { /* file extension to mime */ {NULL, NULL} }; -static inline void -safe_setenv(const char *name, const char *val) -{ - if (val == NULL) - val = ""; - setenv(name, val, 1); -} - __attribute__ ((format (printf, 1, 2))) __attribute__ ((__noreturn__)) static inline void @@ -238,50 +230,7 @@ check_path(struct client *c, const char *path, int *fd return FILE_EXISTS; } -/* - * the inverse of this algorithm, i.e. starting from the start of the - * path + strlen(cgi), and checking if each component, should be - * faster. But it's tedious to write. This does the opposite: starts - * from the end and strip one component at a time, until either an - * executable is found or we emptied the path. - */ int -check_for_cgi(char *path, char *query, struct pollfd *fds, struct client *c) -{ - char *end; - end = strchr(path, '\0'); - - /* NB: assume CGI is enabled and path matches cgi */ - - while (end > path) { - /* go up one level. UNIX paths are simple and POSIX - * dirname, with its ambiguities on if the given path - * is changed or not, gives me headaches. */ - while (*end != '/') - end--; - *end = '\0'; - - switch (check_path(c, path, &c->fd)) { - case FILE_EXECUTABLE: - return start_cgi(path, end+1, query, fds,c); - case FILE_MISSING: - break; - default: - goto err; - } - - *end = '/'; - end--; - } - -err: - if (!start_reply(fds, c, NOT_FOUND, "not found")) - return 0; - goodbye(fds, c); - return 0; -} - -int open_file(char *fpath, char *query, struct pollfd *fds, struct client *c) { switch (check_path(c, fpath, &c->fd)) { @@ -329,179 +278,7 @@ open_file(char *fpath, char *query, struct pollfd *fds } } -int -start_cgi(const char *spath, const char *relpath, const char *query, - struct pollfd *fds, struct client *c) -{ - pid_t pid; - int p[2]; /* read end, write end */ - - if (pipe(p) == -1) - goto err; - - switch (pid = fork()) { - case -1: - goto err; - - case 0: { /* child */ - char *ex, *requri, *portno; - char addr[NI_MAXHOST]; - char *argv[] = { NULL, NULL, NULL }; - int ec; - - close(p[0]); - if (dup2(p[1], 1) == -1) - goto childerr; - - if (inet_ntop(c->af, &c->addr, addr, sizeof(addr)) == NULL) - goto childerr; - - ec = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr), - addr, sizeof(addr), - NULL, 0, - NI_NUMERICHOST | NI_NUMERICSERV); - if (ec != 0) - goto childerr; - - if (asprintf(&portno, "%d", conf.port) == -1) - goto childerr; - - if (asprintf(&ex, "%s/%s", c->host->dir, spath) == -1) - goto childerr; - - if (asprintf(&requri, "%s%s%s", spath, - *relpath == '\0' ? "" : "/", relpath) == -1) - goto childerr; - - argv[0] = argv[1] = ex; - - /* fix the env */ - safe_setenv("GATEWAY_INTERFACE", "CGI/1.1"); - safe_setenv("SERVER_SOFTWARE", "gmid"); - safe_setenv("SERVER_PORT", portno); - /* setenv("SERVER_NAME", "", 1); */ - safe_setenv("SCRIPT_NAME", spath); - safe_setenv("SCRIPT_EXECUTABLE", ex); - safe_setenv("REQUEST_URI", requri); - safe_setenv("REQUEST_RELATIVE", relpath); - safe_setenv("QUERY_STRING", query); - safe_setenv("REMOTE_HOST", addr); - safe_setenv("REMOTE_ADDR", addr); - safe_setenv("DOCUMENT_ROOT", c->host->dir); - - if (tls_peer_cert_provided(c->ctx)) { - safe_setenv("AUTH_TYPE", "Certificate"); - safe_setenv("REMOTE_USER", tls_peer_cert_subject(c->ctx)); - safe_setenv("TLS_CLIENT_ISSUER", tls_peer_cert_issuer(c->ctx)); - safe_setenv("TLS_CLIENT_HASH", tls_peer_cert_hash(c->ctx)); - } - - execvp(ex, argv); - goto childerr; - } - - default: /* parent */ - close(p[1]); - close(c->fd); - c->fd = p[0]; - c->child = pid; - mark_nonblock(c->fd); - c->state = S_SENDING; - handle_cgi(fds, c); - return 0; - } - -err: - if (!start_reply(fds, c, TEMP_FAILURE, "internal server error")) - return 0; - goodbye(fds, c); - return 0; - -childerr: - dprintf(p[1], "%d internal server error\r\n", TEMP_FAILURE); - close(p[1]); - _exit(1); -} - void -cgi_poll_on_child(struct pollfd *fds, struct client *c) -{ - int fd; - - if (c->waiting_on_child) - return; - c->waiting_on_child = 1; - - fds->events = POLLIN; - - fd = fds->fd; - fds->fd = c->fd; - c->fd = fd; -} - -void -cgi_poll_on_client(struct pollfd *fds, struct client *c) -{ - int fd; - - if (!c->waiting_on_child) - return; - c->waiting_on_child = 0; - - fd = fds->fd; - fds->fd = c->fd; - c->fd = fd; -} - -void -handle_cgi(struct pollfd *fds, struct client *c) -{ - ssize_t r; - - /* ensure c->fd is the child and fds->fd the client */ - cgi_poll_on_client(fds, c); - - while (1) { - if (c->len == 0) { - if ((r = read(c->fd, c->sbuf, sizeof(c->sbuf))) == 0) - goto end; - if (r == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - cgi_poll_on_child(fds, c); - return; - } - goto end; - } - c->len = r; - c->off = 0; - } - - while (c->len > 0) { - switch (r = tls_write(c->ctx, c->sbuf + c->off, c->len)) { - case -1: - goto end; - - case TLS_WANT_POLLOUT: - fds->events = POLLOUT; - return; - - case TLS_WANT_POLLIN: - fds->events = POLLIN; - return; - - default: - c->off += r; - c->len -= r; - break; - } - } - } - -end: - goodbye(fds, c); -} - -void send_file(char *path, char *query, struct pollfd *fds, struct client *c) { ssize_t ret, len; blob - 78187876a2995de208d517cf0236249f79c5cfef blob + b595aabb87137c4f2958fbd36c8569e392d6fce1 --- gmid.h +++ gmid.h @@ -130,12 +130,7 @@ ssize_t filesize(int); const char *path_ext(const char*); const char *mime(const char*); int check_path(struct client*, const char*, int*); -int check_for_cgi(char *, char*, struct pollfd*, struct client*); int open_file(char*, char*, struct pollfd*, struct client*); -int start_cgi(const char*, const char*, const char*, struct pollfd*, struct client*); -void cgi_poll_on_child(struct pollfd*, struct client*); -void cgi_poll_on_client(struct pollfd*, struct client*); -void handle_cgi(struct pollfd*, struct client*); void send_file(char*, char*, struct pollfd*, struct client*); void send_dir(char*, struct pollfd*, struct client*); void handle_handshake(struct pollfd*, struct client*); @@ -162,6 +157,13 @@ extern int yylineno; extern int yyparse(void); extern int yylex(void); +/* cgi.c */ +int check_for_cgi(char *, char*, struct pollfd*, struct client*); +int start_cgi(const char*, const char*, const char*, struct pollfd*, struct client*); +void cgi_poll_on_child(struct pollfd*, struct client*); +void cgi_poll_on_client(struct pollfd*, struct client*); +void handle_cgi(struct pollfd*, struct client*); + /* sandbox.c */ void sandbox();