home: hub: tcp80

Download patch

ref: db15b1a63659f3f731f15bbabce7d4257e98ff3d
parent: 4038abade2594bf0d7c80503e9cbe447e6a4899f
author: grobe0ba <grobe0ba@tcp80.org>
date: Fri Mar 17 19:16:39 CDT 2023

cgi support. possibly works correctly.

--- /dev/null
+++ b/.clangd
@@ -1,0 +1,2 @@
+CompileFlags:
+    Add: [-I${PLAN9}/include]
--- a/README.html
+++ b/README.html
@@ -151,6 +151,42 @@
 
 <p style="margin-top: 0; margin-bottom: 0.17in"></p>
 <p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
+<h3><span style="font-size: 10pt"><b>Common Gateway Interface
+</b></span><span style="font-size: 10pt"></span></h3><span style="font-size: 10pt"></span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
+
+<p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
+<span style="font-size: 10pt">The integrated execfs functionality now also supports running applications that
+rely upon the Common Gateway Interface, CGI/1.1 (RFC 3875).
+</span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
+
+<p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
+<span style="font-size: 10pt">To use this, you need some bindings, and a rule (or rules) like the following:
+</span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
+
+<p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
+<span style="font-size: 10pt"><pre><code>/sys/lib/tcp80:
+/cgi-bin/.*.cgi            cgi
+</code></pre></span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
+
+<p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
+<span style="font-size: 10pt"><pre><code>/rc/bin/service/tcp80:
+#!/bin/rc
+aux/stub -d /cgi-bin
+bind /usr/web/cgi-bin /cgi-bin
+exec /bin/tcp80 -r /sys/lib/tcp80 $3 &gt;&gt;[2]/sys/log/httpd/log
+</code></pre></span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
+
+<p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
+<span style="font-size: 10pt">Please note the addition of
+</span><span style="font-size: 10pt"><tt>$3</tt></span><span style="font-size: 10pt">
+in the
+</span><span style="font-size: 10pt"><tt>exec</tt></span><span style="font-size: 10pt">
+line. This lets tcp80 find the IP address of the client, which the CGI/1.1
+specification requires.
+</span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
+
+<p style="margin-top: 0; margin-bottom: 0.17in"></p>
+<p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
 <h2><span style="font-size: 10pt"><b>An Example Configuration (for shithub)
 </b></span><span style="font-size: 10pt"></span></h2><span style="font-size: 10pt"></span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
 
@@ -185,6 +221,24 @@
 <span style="font-size: 10pt"><pre><code>/lib/namespace.httpd:
 bind /mnt/static /usr/web/static
 </code></pre></span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
+
+<p style="margin-top: 0; margin-bottom: 0.17in"></p>
+<p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
+<h2><span style="font-size: 10pt"><b>Oddities in the Documentation and Implementation
+</b></span><span style="font-size: 10pt"></span></h2><span style="font-size: 10pt"></span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
+
+<p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
+<span style="font-size: 10pt">The astute among you may have noticed that we have
+</span><span style="font-size: 10pt"><tt>/lib/namespace.httpd</tt></span><span style="font-size: 10pt">
+but also seem to ignore it in multiple places in the documentation. This can be found in the
+examples revolving around
+</span><span style="font-size: 10pt"><tt>/rc/bin/service/tcp80</tt></span><span style="font-size: 10pt">.
+The reason for this is quite simple;
+</span><span style="font-size: 10pt"><tt>/lib/namespace.httpd</tt></span><span style="font-size: 10pt">
+is used only in untrusted mode, and only for static files. Anything involving execfs or CGI
+is expected to handle setting up the namespace for itself. This allows scripts full run of the
+system, so their first step should always be to isolate themselves as much as possible.
+</span></p><p style="margin-top: 0; margin-bottom: 0.17in"></p>
 
 <p style="margin-top: 0; margin-bottom: 0.17in"></p>
 <p style="line-height: 1.2em; text-indent: 0.00in; margin-top: 0; margin-bottom: 0; text-align: justify;">
--- a/README.ms
+++ b/README.ms
@@ -160,6 +160,39 @@
 .ihtml a
 file for an example.
 
+.ihtml h3 <h3>
+.NL
+.R
+.SH
+Common Gateway Interface
+.R
+.ihtml h3
+
+The integrated execfs functionality now also supports running applications that
+rely upon the Common Gateway Interface, CGI/1.1 (RFC 3875).
+
+To use this, you need some bindings, and a rule (or rules) like the following:
+
+.ihtml pre <pre>
+.ihtml code <code>
+/sys/lib/tcp80:
+/cgi-bin/.*\.cgi            cgi
+
+/rc/bin/service/tcp80:
+#!/bin/rc
+aux/stub -d /cgi-bin
+bind /usr/web/cgi-bin /cgi-bin
+exec /bin/tcp80 -r /sys/lib/tcp80 $3 >>[2]/sys/log/httpd/log
+.ihtml code
+.ihtml pre
+
+Please note the addition of
+.CW "$3
+in the
+.CW "exec
+line. This lets tcp80 find the IP address of the client, which the CGI/1.1
+specification requires.
+
 .ihtml h2 <h2>
 .NL
 .R
@@ -197,6 +230,25 @@
 bind /mnt/static /usr/web/static
 .ihtml code
 .ihtml pre
+
+.ihtml h2 <h2>
+.NL
+.R
+.SH
+Oddities in the Documentation and Implementation
+.R
+.ihtml h2
+
+The astute among you may have noticed that we have
+.CW "/lib/namespace.httpd
+but also seem to ignore it in multiple places in the documentation. This can be found in the
+examples revolving around
+.CW "/rc/bin/service/tcp80".
+The reason for this is quite simple;
+.CW /lib/namespace.httpd
+is used only in untrusted mode, and only for static files. Anything involving execfs or CGI
+is expected to handle setting up the namespace for itself. This allows scripts full run of the
+system, so their first step should always be to isolate themselves as much as possible.
 
 .ihtml h2 <h2>
 .NL
--- a/tcp80.c
+++ b/tcp80.c
@@ -162,6 +162,26 @@
     505, "HTTP Version not supported",
 };
 
+void createenv(char *env, char *data);
+int dircmp(Dir *a, Dir *b);
+int dispatch(void);
+void dispatchrule(char *cmd);
+Pair *findhdr(Pair *h, char *key);
+char *findrule(char *rulesfile, char *path);
+char *fullurl(char *host, char *path, char *name, char *query);
+void getresponse(char *dst, int bufsz, int code);
+long hdate(char *s);
+void headers(char *path, Dir *d);
+int isleap(int year);
+void main(int argc, char **argv);
+char *nstrcpy(char *d, char *s, int n);
+int parsequery(void);
+int redirerr(int status, Dir *d);
+void respond(char *status);
+char *token(char *s, char *delim, char **pe);
+char *urldec(char *d, char *s, int n);
+char *urlenc(char *d, char *s, int n);
+
 void
 getresponse(char *dst, int bufsz, int code)
 {
@@ -221,11 +241,86 @@
 }
 
 void
+createenv(char *env, char *data)
+{
+	static char statbuf[32];
+	int fd;
+	char *path = "/env/";
+	int plen   = strlen(path);
+	int elen   = strlen(env);
+	int flen   = plen + elen;
+	int dlen   = strlen(data);
+	char *filename;
+
+	if((filename = malloc(plen + elen + 1)) == nil){
+		getresponse(statbuf, 32, 500);
+		respond(statbuf);
+		sysfatal("malloc");
+	}
+	nstrcpy(filename, path, flen);
+	strncat(filename, env, elen);
+
+	if((fd = create(filename, OWRITE, 0644)) < 0){
+		getresponse(statbuf, 32, 500);
+		respond(statbuf);
+		sysfatal("create");
+	}
+	if(write(fd, data, dlen) != dlen){
+		getresponse(statbuf, 32, 500);
+		respond(statbuf);
+		sysfatal("write");
+	}
+	close(fd);
+	free(filename);
+	return;
+}
+
+void
 dispatchrule(char *cmd)
 {
-	if(rfork(RFPROC | RFNOWAIT | RFFDG | RFREND) == 0){
+	static char statbuf[32];
+	if(rfork(RFPROC | RFNOWAIT | RFFDG | RFREND | RFENVG) == 0){
+		if(cistrcmp(cmd, "cgi") == 0){
+			char *query = strchr(location, '?');
+			if(query != nil){
+				query[0] = 0;
+				query++;
+			}
+			createenv("GATEWAY_INTERFACE", "CGI/1.1");
+			createenv("QUERY_STRING", query ? query : "");
+			createenv("REMOTE_ADDR", remote);
+			createenv("REQUEST_METHOD", method);
+			createenv("REQUEST_URI", location);
+			createenv("SCRIPT_NAME", location);
+			Pair *host = findhdr(nil, "Host");
+			if(host != nil){
+				createenv("SERVER_NAME", host->val);
+			}else{
+				createenv("SERVER_NAME", "localhost");
+			}
+			// This is a nasty lie. It could be any port, but we don't actually know
+			createenv("SERVER_PORT", "80");
+			createenv("SERVER_PROTOCOL", "HTTP");
+			createenv("SERVER_SOFTWARE", "tcp80");
+			chdir("/usr/web/bin");
+			const char* base = "/usr/web";
+			const int blen = strlen(base);
+			const int llen = strlen(location);
+			char *newloc = malloc(blen+llen+1);
+			nstrcpy(newloc, base, blen+llen+1);
+			strncat(newloc, location, blen+llen+1);
+			newloc[blen+llen] = 0;
+			execl("/bin/rc", "rc", "-c", newloc, nil);
+
+			getresponse(statbuf, 32, 500);
+			respond(statbuf);
+			sysfatal("exec");
+		}
 		execl("/bin/rc", "rc", "-c", cmd, nil);
-		exits("exec");
+
+		getresponse(statbuf, 32, 500);
+		respond(statbuf);
+		sysfatal("exec");
 	}
 	exits(nil);
 }
@@ -569,7 +664,8 @@
 
 			h = findhdr(nil, "Host");
 			p = strchr(location, '?');
-			s = fullurl(h ? h->val : nil, urlenc(tmp, buf, sizeof(tmp)), "/", p ? p + 1 : nil);
+			s = fullurl(h ? h->val : nil, urlenc(tmp, buf, sizeof(tmp)), "/",
+				    p ? p + 1 : nil);
 			if(!nobody)
 				n = snprint(buf, sizeof(buf),
 					    "<html><head><title>%s</title></head>\n"
@@ -879,9 +975,11 @@
 			if(cistrcmp(method, "GET") != 0)
 				if(redirerr(405, &fakedir))
 					goto rOut;
-			getresponse(statbuf, 32, 200);
-			respond(statbuf);
-			headers(loc, &fakedir);
+			if(cistrcmp(c, "cgi") != 0){
+				getresponse(statbuf, 32, 200);
+				respond(statbuf);
+				headers(loc, &fakedir);
+			}
 			dispatchrule(c);
 		rOut:
 			free(c);