ref: db15b1a63659f3f731f15bbabce7d4257e98ff3d
parent: 4038abade2594bf0d7c80503e9cbe447e6a4899f
author: grobe0ba <[email protected]>
date: Fri Mar 17 20:16:39 EDT 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 >>[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);