Commit Diff


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 <op@omarpolo.com>
+ *
+ * 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 <netdb.h>
+
+#include <errno.h>
+#include <string.h>
+
+#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();