xref: /plan9-contrib/sys/src/cmd/ip/httpd/9down.c (revision 26efc675cd2a637181faaecfb9746792ff0dfdd8)
1433d7a01SDavid du Colombier /*
2433d7a01SDavid du Colombier  * Serve files from the Plan 9 distribution.
3433d7a01SDavid du Colombier  * Check that we're not giving out files to bad countries
4433d7a01SDavid du Colombier  * and then just handle the request normally.
5433d7a01SDavid du Colombier  * Beware: behaviour changes based on argv[0].
6433d7a01SDavid du Colombier  * Modified to serve /sys/src, not /n/sources.
7433d7a01SDavid du Colombier  */
8433d7a01SDavid du Colombier #include <u.h>
9433d7a01SDavid du Colombier #include <libc.h>
10433d7a01SDavid du Colombier #include <bio.h>
11433d7a01SDavid du Colombier #include <ndb.h>
12433d7a01SDavid du Colombier #include <ip.h>
13433d7a01SDavid du Colombier #include <libsec.h>
14433d7a01SDavid du Colombier #include <auth.h>
15433d7a01SDavid du Colombier #include "httpd.h"
16433d7a01SDavid du Colombier #include "httpsrv.h"
17433d7a01SDavid du Colombier 
18433d7a01SDavid du Colombier #define LOG "9down"
19433d7a01SDavid du Colombier 
20433d7a01SDavid du Colombier #ifndef OUTSIDE
21*26efc675SDavid du Colombier #define OUTSIDE 0
22433d7a01SDavid du Colombier #endif
23433d7a01SDavid du Colombier 
24433d7a01SDavid du Colombier static	Hio	houtb;
25433d7a01SDavid du Colombier static	Hio	*hout;
26433d7a01SDavid du Colombier static	HConnect*connect;
27433d7a01SDavid du Colombier 
28433d7a01SDavid du Colombier enum
29433d7a01SDavid du Colombier {
30433d7a01SDavid du Colombier 	MNAMELEN	= 64,
31433d7a01SDavid du Colombier };
32433d7a01SDavid du Colombier 
33433d7a01SDavid du Colombier void	cat(char*, int);
34433d7a01SDavid du Colombier void	filter(char*, char*, char*, char*, char*);
35433d7a01SDavid du Colombier HRange*	myfixrange(HConnect*, Dir*, int);
36433d7a01SDavid du Colombier void	error(char*, ...);
37433d7a01SDavid du Colombier void	interr(char*, ...);
38433d7a01SDavid du Colombier void	system(char*, ...);
39433d7a01SDavid du Colombier 
40433d7a01SDavid du Colombier int sources;
41433d7a01SDavid du Colombier 
42433d7a01SDavid du Colombier void
main(int argc,char ** argv)43433d7a01SDavid du Colombier main(int argc, char **argv)
44433d7a01SDavid du Colombier {
45433d7a01SDavid du Colombier 	int trailingslash;
46433d7a01SDavid du Colombier 	HSPriv *hp;
47433d7a01SDavid du Colombier 	char *file, *dir;
48433d7a01SDavid du Colombier 	char ok[256];
49433d7a01SDavid du Colombier 
50433d7a01SDavid du Colombier 	quotefmtinstall();
51433d7a01SDavid du Colombier 	fmtinstall('H', httpfmt);
52433d7a01SDavid du Colombier 	fmtinstall('U', hurlfmt);
53433d7a01SDavid du Colombier 
54433d7a01SDavid du Colombier 	connect = init(argc, argv);
55433d7a01SDavid du Colombier 	hout = &connect->hout;
56433d7a01SDavid du Colombier 	if(hparseheaders(connect, HSTIMEOUT) < 0)
57433d7a01SDavid du Colombier 		exits("failed");
58433d7a01SDavid du Colombier 
59433d7a01SDavid du Colombier 	if(strcmp(connect->req.meth, "GET") != 0 && strcmp(connect->req.meth, "HEAD") != 0){
60433d7a01SDavid du Colombier 		hunallowed(connect, "GET, HEAD");
61433d7a01SDavid du Colombier 		exits("unallowed");
62433d7a01SDavid du Colombier 	}
63433d7a01SDavid du Colombier 	if(connect->head.expectother || connect->head.expectcont){
64433d7a01SDavid du Colombier 		hfail(connect, HExpectFail, nil);
65433d7a01SDavid du Colombier 		exits("failed");
66433d7a01SDavid du Colombier 	}
67433d7a01SDavid du Colombier 
68433d7a01SDavid du Colombier 	hp = connect->private;
69433d7a01SDavid du Colombier 	/* rename 9down to sources to display sources tree! */
70433d7a01SDavid du Colombier 	if(strstr(argv[0], "sources"))
71433d7a01SDavid du Colombier 		sources = 1;
72433d7a01SDavid du Colombier 	syslog(0, LOG, "%s %s ver %d.%d uri %s search %s",
73433d7a01SDavid du Colombier 		sources ? "sources" : "9down",
74433d7a01SDavid du Colombier 		hp->remotesys,
75433d7a01SDavid du Colombier 		connect->req.vermaj, connect->req.vermin,
76433d7a01SDavid du Colombier 		connect->req.uri, connect->req.search);
77433d7a01SDavid du Colombier 
78433d7a01SDavid du Colombier 	file = strdup(connect->req.uri);
79433d7a01SDavid du Colombier 
80*26efc675SDavid du Colombier 	dir = "/usr/web";
81433d7a01SDavid du Colombier 	if(sources)
82*26efc675SDavid du Colombier 		dir = "/usr/web/sources";
83433d7a01SDavid du Colombier 	if(chdir(dir) < 0)
84433d7a01SDavid du Colombier 		interr("cd %s: %r", dir);
85433d7a01SDavid du Colombier 
86433d7a01SDavid du Colombier 	trailingslash = (file[0] && file[strlen(file)-1] == '/');
87433d7a01SDavid du Colombier 	if(file[0] == 0)
88433d7a01SDavid du Colombier 		file = strdup("/");
89433d7a01SDavid du Colombier 	cleanname(file);
90433d7a01SDavid du Colombier 	if(file[0] == '/'){
91433d7a01SDavid du Colombier 		if(file[1])
92433d7a01SDavid du Colombier 			file++;
93433d7a01SDavid du Colombier 		else
94433d7a01SDavid du Colombier 			file = ".";
95433d7a01SDavid du Colombier 	}
96433d7a01SDavid du Colombier 	/* now file is not rooted, no dot dots */
97433d7a01SDavid du Colombier 
98433d7a01SDavid du Colombier 	if(file[0] == '#')
99433d7a01SDavid du Colombier 		interr("bad file %s", file);
100433d7a01SDavid du Colombier 	if(access(file, AEXIST) < 0)
101433d7a01SDavid du Colombier 		error("404 %s not found", file);
102433d7a01SDavid du Colombier 	if (OUTSIDE) {
103433d7a01SDavid du Colombier 		if(access("/mnt/ipok/ok", AEXIST) < 0){
104433d7a01SDavid du Colombier 			system("mount /srv/ipok /mnt/ipok >[2]/dev/null");
105433d7a01SDavid du Colombier 			if(access("/mnt/ipok/ok", AEXIST) < 0)
106433d7a01SDavid du Colombier 				interr("no /mnt/ipok/ok");
107433d7a01SDavid du Colombier 		}
108433d7a01SDavid du Colombier 		snprint(ok, sizeof ok, "/mnt/ipok/ok/%s", hp->remotesys);
109433d7a01SDavid du Colombier 		if(access(ok, AEXIST) < 0){
110433d7a01SDavid du Colombier 			syslog(0, LOG, "reject %s %s",
111433d7a01SDavid du Colombier 				hp->remotesys, connect->req.uri);
112433d7a01SDavid du Colombier 			file = "/sys/lib/dist/web/err/prohibited.html";
113433d7a01SDavid du Colombier 		}
114433d7a01SDavid du Colombier 	}
115433d7a01SDavid du Colombier 	cat(file, trailingslash);
116433d7a01SDavid du Colombier 	hflush(hout);
117433d7a01SDavid du Colombier 	exits(nil);
118433d7a01SDavid du Colombier }
119433d7a01SDavid du Colombier 
120433d7a01SDavid du Colombier static void
doerr(char * msg)121433d7a01SDavid du Colombier doerr(char *msg)
122433d7a01SDavid du Colombier {
123433d7a01SDavid du Colombier 	hprint(hout, "%s %s\r\n", hversion, msg);
124433d7a01SDavid du Colombier 	writelog(connect, "Reply: %s\n", msg);
125433d7a01SDavid du Colombier 	hflush(hout);
126433d7a01SDavid du Colombier 	exits(nil);
127433d7a01SDavid du Colombier }
128433d7a01SDavid du Colombier 
129433d7a01SDavid du Colombier /* must pass an http error code then a message, e.g., 404 not found */
130433d7a01SDavid du Colombier void
error(char * fmt,...)131433d7a01SDavid du Colombier error(char *fmt, ...)
132433d7a01SDavid du Colombier {
133433d7a01SDavid du Colombier 	char *buf;
134433d7a01SDavid du Colombier 	va_list arg;
135433d7a01SDavid du Colombier 
136433d7a01SDavid du Colombier 	va_start(arg, fmt);
137433d7a01SDavid du Colombier 	buf = vsmprint(fmt, arg);
138433d7a01SDavid du Colombier 	va_end(arg);
139433d7a01SDavid du Colombier 
140433d7a01SDavid du Colombier 	syslog(0, LOG, "error: %s", buf);
141433d7a01SDavid du Colombier 	doerr(buf);
142433d7a01SDavid du Colombier }
143433d7a01SDavid du Colombier 
144433d7a01SDavid du Colombier /* must not pass an http error code, just a message */
145433d7a01SDavid du Colombier void
interr(char * fmt,...)146433d7a01SDavid du Colombier interr(char *fmt, ...)
147433d7a01SDavid du Colombier {
148433d7a01SDavid du Colombier 	char *buf;
149433d7a01SDavid du Colombier 	va_list arg;
150433d7a01SDavid du Colombier 
151433d7a01SDavid du Colombier 	va_start(arg, fmt);
152433d7a01SDavid du Colombier 	buf = vsmprint(fmt, arg);
153433d7a01SDavid du Colombier 	va_end(arg);
154433d7a01SDavid du Colombier 
155433d7a01SDavid du Colombier 	syslog(0, LOG, "internal error: %s", buf);
156433d7a01SDavid du Colombier 	doerr("500 Internal Error");
157433d7a01SDavid du Colombier }
158433d7a01SDavid du Colombier 
159433d7a01SDavid du Colombier void
system(char * fmt,...)160433d7a01SDavid du Colombier system(char *fmt, ...)
161433d7a01SDavid du Colombier {
162433d7a01SDavid du Colombier 	char buf[512];
163433d7a01SDavid du Colombier 	va_list arg;
164433d7a01SDavid du Colombier 
165433d7a01SDavid du Colombier 	va_start(arg, fmt);
166433d7a01SDavid du Colombier 	vsnprint(buf, sizeof buf, fmt, arg);
167433d7a01SDavid du Colombier 	va_end(arg);
168433d7a01SDavid du Colombier 
169433d7a01SDavid du Colombier 	if(fork() == 0){
170433d7a01SDavid du Colombier 		execl("/bin/rc", "rc", "-c", buf, nil);
171433d7a01SDavid du Colombier 		_exits("execl failed");
172433d7a01SDavid du Colombier 	}
173433d7a01SDavid du Colombier 	waitpid();
174433d7a01SDavid du Colombier }
175433d7a01SDavid du Colombier 
176433d7a01SDavid du Colombier void
cat(char * file,int trailingslash)177433d7a01SDavid du Colombier cat(char *file, int trailingslash)
178433d7a01SDavid du Colombier {
179433d7a01SDavid du Colombier 	int fd, m;
180433d7a01SDavid du Colombier 	char *reply;
181433d7a01SDavid du Colombier 	long rem;
182433d7a01SDavid du Colombier 	HRange rr;
183433d7a01SDavid du Colombier 	Dir *d;
184433d7a01SDavid du Colombier 	HRange *r;
185433d7a01SDavid du Colombier 	HConnect *c;
186433d7a01SDavid du Colombier 	HSPriv *hp;
187433d7a01SDavid du Colombier 	char buf[8192];
188433d7a01SDavid du Colombier 
189433d7a01SDavid du Colombier 	c = connect;
190433d7a01SDavid du Colombier 	if((fd = open(file, OREAD)) < 0){
191433d7a01SDavid du Colombier 	notfound:
192433d7a01SDavid du Colombier 		hprint(hout, "%s 404 Not Found\r\n", hversion);
193433d7a01SDavid du Colombier 		writelog(c, "Reply: 404 Not Found\n");
194433d7a01SDavid du Colombier 		return;
195433d7a01SDavid du Colombier 	}
196433d7a01SDavid du Colombier 
197433d7a01SDavid du Colombier 	d = dirfstat(fd);
198433d7a01SDavid du Colombier 	if(d == nil){
199433d7a01SDavid du Colombier 		close(fd);
200433d7a01SDavid du Colombier 		goto notfound;
201433d7a01SDavid du Colombier 	}
202433d7a01SDavid du Colombier 
203433d7a01SDavid du Colombier 	if(sources){
204433d7a01SDavid du Colombier 		if((d->mode&DMDIR) && !trailingslash){
205433d7a01SDavid du Colombier 			hprint(hout, "%s 301 Moved Permanently\r\n", hversion);
206433d7a01SDavid du Colombier 			hprint(hout, "Date: %D\r\n", time(nil));
207433d7a01SDavid du Colombier 			hprint(hout, "Connection: close\r\n");
208433d7a01SDavid du Colombier 			if(strcmp(file, ".") == 0)
209*26efc675SDavid du Colombier 				hprint(hout, "Location: /sources/\r\n");
210433d7a01SDavid du Colombier 			else
211*26efc675SDavid du Colombier 				hprint(hout, "Location: /sources/%s/\r\n", file);
212433d7a01SDavid du Colombier 			hprint(hout, "\r\n");
213433d7a01SDavid du Colombier 			hflush(hout);
214433d7a01SDavid du Colombier 			exits(0);
215433d7a01SDavid du Colombier 		}
216433d7a01SDavid du Colombier 		hprint(hout, "%s 200 OK\r\n", hversion);
217433d7a01SDavid du Colombier 		hprint(hout, "Server: Plan9\r\n");
218433d7a01SDavid du Colombier 		hprint(hout, "Date: %D\r\n", time(nil));
219433d7a01SDavid du Colombier 		hprint(hout, "Connection: close\r\n");
220433d7a01SDavid du Colombier 		hprint(hout, "Last-Modified: %D\r\n", d->mtime);
221433d7a01SDavid du Colombier 		hflush(hout);
222433d7a01SDavid du Colombier 		dup(hout->fd, 1);
223433d7a01SDavid du Colombier 		dup(hout->fd, 2);
224433d7a01SDavid du Colombier 		system("/sys/lib/dist/sources2web %q", file);
225433d7a01SDavid du Colombier 		exits(0);
226433d7a01SDavid du Colombier 	}
227433d7a01SDavid du Colombier 
228433d7a01SDavid du Colombier 	if(d->mode&DMDIR)
229433d7a01SDavid du Colombier 		goto notfound;
230433d7a01SDavid du Colombier 
231433d7a01SDavid du Colombier 	r = myfixrange(connect, d, 0);
232433d7a01SDavid du Colombier 	hflush(hout);
233433d7a01SDavid du Colombier 	if(r){
234433d7a01SDavid du Colombier 		reply = "206";
235433d7a01SDavid du Colombier 		if(seek(fd, r->start, 0) < 0)
236433d7a01SDavid du Colombier 			syslog(0, LOG, "seek: %r");
237433d7a01SDavid du Colombier 		rem = r->stop-r->start+1;
238433d7a01SDavid du Colombier 	} else {
239433d7a01SDavid du Colombier 		reply = "200";
240433d7a01SDavid du Colombier 		rr.start = 0;
241433d7a01SDavid du Colombier 		rr.stop = d->length-1;
242433d7a01SDavid du Colombier 		r = &rr;
243433d7a01SDavid du Colombier 		rem = d->length;
244433d7a01SDavid du Colombier 	}
245433d7a01SDavid du Colombier 	for(; rem > 0; rem -= m){
246433d7a01SDavid du Colombier 		m = read(fd, buf, sizeof(buf));
247433d7a01SDavid du Colombier 		if(m <= 0)
248433d7a01SDavid du Colombier 			break;
249433d7a01SDavid du Colombier 		if(m > rem)
250433d7a01SDavid du Colombier 			m = rem;
251433d7a01SDavid du Colombier 		if(hwrite(hout, buf, m) != m)
252433d7a01SDavid du Colombier 			break;
253433d7a01SDavid du Colombier 	}
254433d7a01SDavid du Colombier 	hp = c->private;
255433d7a01SDavid du Colombier 	syslog(0, LOG, "%s: rs %s %lud-%lud/%lud-%lud %s", file, hp->remotesys,
256433d7a01SDavid du Colombier 		r->start, r->stop+1-rem,
257433d7a01SDavid du Colombier 		r->start, r->stop+1,
258433d7a01SDavid du Colombier 		c->head.client);
259433d7a01SDavid du Colombier 	writelog(c, "Reply: %s\n", reply);
260433d7a01SDavid du Colombier 	close(fd);
261433d7a01SDavid du Colombier 	return;
262433d7a01SDavid du Colombier }
263433d7a01SDavid du Colombier 
264433d7a01SDavid du Colombier HRange*
myfixrange(HConnect * c,Dir * d,int align)265433d7a01SDavid du Colombier myfixrange(HConnect *c, Dir *d, int align)
266433d7a01SDavid du Colombier {
267433d7a01SDavid du Colombier 	HRange *r, *rv;
268433d7a01SDavid du Colombier 
269433d7a01SDavid du Colombier 	if(!c->req.vermaj)
270433d7a01SDavid du Colombier 		return nil;
271433d7a01SDavid du Colombier 
272433d7a01SDavid du Colombier 	rv = nil;
273433d7a01SDavid du Colombier 	r = c->head.range;
274433d7a01SDavid du Colombier 
275433d7a01SDavid du Colombier 	if(r == nil)
276433d7a01SDavid du Colombier 		goto out;
277433d7a01SDavid du Colombier 
278433d7a01SDavid du Colombier 	if(c->head.ifrangeetag != nil)
279433d7a01SDavid du Colombier 		goto out;
280433d7a01SDavid du Colombier 
281433d7a01SDavid du Colombier 	if(c->head.ifrangedate != 0 && c->head.ifrangedate != d->mtime)
282433d7a01SDavid du Colombier 		goto out;
283433d7a01SDavid du Colombier 
284433d7a01SDavid du Colombier 	if(d->length == 0)
285433d7a01SDavid du Colombier 		goto out;
286433d7a01SDavid du Colombier 
287433d7a01SDavid du Colombier 	/* we only support a single range */
288433d7a01SDavid du Colombier 	if(r->next != nil)
289433d7a01SDavid du Colombier 		goto out;
290433d7a01SDavid du Colombier 
291433d7a01SDavid du Colombier 	if(r->suffix){
292433d7a01SDavid du Colombier 		r->start = d->length - r->stop;
293433d7a01SDavid du Colombier 		if(r->start >= d->length)
294433d7a01SDavid du Colombier 			r->start = 0;
295433d7a01SDavid du Colombier 		r->stop = d->length - 1;
296433d7a01SDavid du Colombier 		r->suffix = 0;
297433d7a01SDavid du Colombier 	}
298433d7a01SDavid du Colombier 	if(r->stop >= d->length)
299433d7a01SDavid du Colombier 		r->stop = d->length - 1;
300433d7a01SDavid du Colombier 	if(r->start > r->stop)
301433d7a01SDavid du Colombier 		goto out;
302433d7a01SDavid du Colombier 	if(align && (r->start%512) != 0)
303433d7a01SDavid du Colombier 		goto out;
304433d7a01SDavid du Colombier 
305433d7a01SDavid du Colombier 	rv = r;
306433d7a01SDavid du Colombier 
307433d7a01SDavid du Colombier out:
308433d7a01SDavid du Colombier 	if(rv != nil)
309433d7a01SDavid du Colombier 		hprint(hout, "%s 206 Partial Content\r\n", hversion);
310433d7a01SDavid du Colombier 	else
311433d7a01SDavid du Colombier 		hprint(hout, "%s 200 OK\r\n", hversion);
312433d7a01SDavid du Colombier 	hprint(hout, "Server: Plan9\r\n");
313433d7a01SDavid du Colombier 	hprint(hout, "Date: %D\r\n", time(nil));
314433d7a01SDavid du Colombier 	hprint(hout, "Connection: close\r\n");
315433d7a01SDavid du Colombier 	hprint(hout, "Content-type: application/octet-stream\r\n");
316433d7a01SDavid du Colombier 	if(rv != nil){
317433d7a01SDavid du Colombier 		hprint(hout, "Content-Range: bytes %uld-%uld/%lld\r\n", r->start, r->stop,
318433d7a01SDavid du Colombier 			d->length);
319433d7a01SDavid du Colombier 		hprint(hout, "Content-Length: %uld\r\n", r->stop-r->start+1);
320433d7a01SDavid du Colombier 	}else
321433d7a01SDavid du Colombier 		hprint(hout, "Content-Length: %lld\r\n", d->length);
322433d7a01SDavid du Colombier 	hprint(hout, "Last-Modified: %D\r\n", d->mtime);
323433d7a01SDavid du Colombier 	hprint(hout, "\r\n");
324433d7a01SDavid du Colombier 	return rv;
325433d7a01SDavid du Colombier }
326433d7a01SDavid du Colombier 
327433d7a01SDavid du Colombier 
328