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