Change the magic number used to introduce a trie file, so that instead
[sgt/agedu] / httpd.c
CommitLineData
70322ae3 1/*
2 * httpd.c: implementation of httpd.h.
3 */
4
bf53e756 5#include "agedu.h"
995db599 6#include "alloc.h"
70322ae3 7#include "html.h"
812e4bf2 8#include "httpd.h"
70322ae3 9
10/* --- Logic driving what the web server's responses are. --- */
11
812e4bf2 12enum { /* connctx states */
13 READING_REQ_LINE,
14 READING_HEADERS,
15 DONE
16};
17
70322ae3 18struct connctx {
19 const void *t;
20 char *data;
21 int datalen, datasize;
812e4bf2 22 char *method, *url, *headers, *auth;
23 int state;
70322ae3 24};
25
26/*
27 * Called when a new connection arrives on a listening socket.
28 * Returns a connctx for the new connection.
29 */
30struct connctx *new_connection(const void *t)
31{
32 struct connctx *cctx = snew(struct connctx);
33 cctx->t = t;
34 cctx->data = NULL;
35 cctx->datalen = cctx->datasize = 0;
812e4bf2 36 cctx->state = READING_REQ_LINE;
37 cctx->method = cctx->url = cctx->headers = cctx->auth = NULL;
70322ae3 38 return cctx;
39}
40
41void free_connection(struct connctx *cctx)
42{
43 sfree(cctx->data);
44 sfree(cctx);
45}
46
812e4bf2 47static char *http_error(char *code, char *errmsg, char *extraheader,
48 char *errtext, ...)
70322ae3 49{
50 return dupfmt("HTTP/1.1 %s %s\r\n"
51 "Date: %D\r\n"
bf53e756 52 "Server: " PNAME "\r\n"
70322ae3 53 "Connection: close\r\n"
812e4bf2 54 "%s"
70322ae3 55 "Content-Type: text/html; charset=US-ASCII\r\n"
56 "\r\n"
57 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
58 "<HTML><HEAD>\r\n"
59 "<TITLE>%s %s</TITLE>\r\n"
60 "</HEAD><BODY>\r\n"
61 "<H1>%s %s</H1>\r\n"
62 "<P>%s</P>\r\n"
63 "</BODY></HTML>\r\n", code, errmsg,
812e4bf2 64 extraheader ? extraheader : "",
70322ae3 65 code, errmsg, code, errmsg, errtext);
66}
67
68static char *http_success(char *mimetype, int stuff_cr, char *document)
69{
70 return dupfmt("HTTP/1.1 200 OK\r\n"
71 "Date: %D\r\n"
72 "Expires: %D\r\n"
bf53e756 73 "Server: " PNAME "\r\n"
70322ae3 74 "Connection: close\r\n"
75 "Content-Type: %s\r\n"
76 "\r\n"
77 "%S", mimetype, stuff_cr, document);
78}
79
80/*
81 * Called when data comes in on a connection.
82 *
83 * If this function returns NULL, the platform code continues
84 * reading from the socket. Otherwise, it returns some dynamically
85 * allocated data which the platform code will then write to the
86 * socket before closing it.
87 */
812e4bf2 88char *got_data(struct connctx *ctx, char *data, int length,
f2e52893 89 int magic_access, const char *auth_string,
90 const struct html_config *cfg)
70322ae3 91{
812e4bf2 92 char *line, *p, *q, *r, *z1, *z2, c1, c2;
a8d1009f 93 int auth_correct = 0;
70322ae3 94 unsigned long index;
95 char *document, *ret;
96
812e4bf2 97 /*
98 * Add the data we've just received to our buffer.
99 */
70322ae3 100 if (ctx->datasize < ctx->datalen + length) {
101 ctx->datasize = (ctx->datalen + length) * 3 / 2 + 4096;
102 ctx->data = sresize(ctx->data, ctx->datasize, char);
103 }
104 memcpy(ctx->data + ctx->datalen, data, length);
105 ctx->datalen += length;
106
107 /*
812e4bf2 108 * Gradually process the HTTP request as we receive it.
70322ae3 109 */
812e4bf2 110 if (ctx->state == READING_REQ_LINE) {
111 /*
112 * We're waiting for the first line of the input, which
113 * contains the main HTTP request. See if we've got it
114 * yet.
115 */
70322ae3 116
812e4bf2 117 line = ctx->data;
118 /*
119 * RFC 2616 section 4.1: `In the interest of robustness,
120 * [...] if the server is reading the protocol stream at
121 * the beginning of a message and receives a CRLF first,
122 * it should ignore the CRLF.'
123 */
124 while (line - ctx->data < ctx->datalen &&
125 (*line == '\r' || *line == '\n'))
126 line++;
127 q = line;
128 while (q - ctx->data < ctx->datalen && *q != '\n')
129 q++;
130 if (q - ctx->data >= ctx->datalen)
131 return NULL; /* not got request line yet */
70322ae3 132
812e4bf2 133 /*
134 * We've got the first line of the request. Zero-terminate
135 * and parse it into method, URL and optional HTTP
136 * version.
137 */
138 *q = '\0';
139 ctx->headers = q+1;
140 if (q > line && q[-1] == '\r')
141 *--q = '\0';
142 z1 = z2 = q;
143 c1 = c2 = *q;
144 p = line;
145 while (*p && !isspace((unsigned char)*p)) p++;
146 if (*p) {
147 z1 = p++;
148 c1 = *z1;
149 *z1 = '\0';
150 }
151 while (*p && isspace((unsigned char)*p)) p++;
152 q = p;
153 while (*q && !isspace((unsigned char)*q)) q++;
154 z2 = q++;
155 c2 = *z2;
156 *z2 = '\0';
157 while (*q && isspace((unsigned char)*q)) q++;
158
159 /*
160 * Now `line' points at the method name; p points at the
161 * URL, if any; q points at the HTTP version, if any.
162 */
163
164 /*
165 * There should _be_ a URL, on any request type at all.
166 */
167 if (!*p) {
168 char *ret, *text;
169 /* Restore the request to the way we received it. */
170 *z2 = c2;
171 *z1 = c1;
bf53e756 172 text = dupfmt("<code>" PNAME "</code> received the HTTP request"
812e4bf2 173 " \"<code>%h</code>\", which contains no URL.",
174 line);
175 ret = http_error("400", "Bad request", NULL, text);
176 sfree(text);
177 return ret;
178 }
179
180 ctx->method = line;
181 ctx->url = p;
182
183 /*
184 * If there was an HTTP version, we might need to see
185 * headers. Otherwise, the request is done.
186 */
187 if (*q) {
188 ctx->state = READING_HEADERS;
189 } else {
190 ctx->state = DONE;
191 }
70322ae3 192 }
70322ae3 193
812e4bf2 194 if (ctx->state == READING_HEADERS) {
195 /*
196 * While we're receiving the HTTP request headers, all we
197 * do is to keep scanning to see if we find two newlines
198 * next to each other.
199 */
200 q = ctx->data + ctx->datalen;
201 for (p = ctx->headers; p < q; p++) {
202 if (*p == '\n' &&
203 ((p+1 < q && p[1] == '\n') ||
204 (p+2 < q && p[1] == '\r' && p[2] == '\n'))) {
205 p[1] = '\0';
206 ctx->state = DONE;
207 break;
208 }
209 }
70322ae3 210 }
211
812e4bf2 212 if (ctx->state == DONE) {
213 /*
214 * Now we have the entire HTTP request. Decide what to do
215 * with it.
216 */
217 if (auth_string) {
218 /*
219 * Search the request headers for Authorization.
220 */
221 q = ctx->data + ctx->datalen;
222 for (p = ctx->headers; p < q; p++) {
223 const char *hdr = "Authorization:";
224 int i;
225 for (i = 0; hdr[i]; i++) {
226 if (p >= q || tolower((unsigned char)*p) !=
227 tolower((unsigned char)hdr[i]))
228 break;
229 p++;
230 }
231 if (!hdr[i])
232 break; /* found our header */
233 p = memchr(p, '\n', q - p);
234 if (!p)
235 p = q;
236 }
237 if (p < q) {
812e4bf2 238 while (p < q && isspace((unsigned char)*p))
239 p++;
240 r = p;
241 while (p < q && !isspace((unsigned char)*p))
242 p++;
243 if (p < q) {
244 *p++ = '\0';
245 if (!strcasecmp(r, "Basic")) {
246 while (p < q && isspace((unsigned char)*p))
247 p++;
248 r = p;
249 while (p < q && !isspace((unsigned char)*p))
250 p++;
251 if (p < q) {
252 *p++ = '\0';
253 if (!strcmp(r, auth_string))
254 auth_correct = 1;
255 }
256 }
257 }
258 }
259 }
260
261 if (!magic_access && !auth_correct) {
5a830bf8 262 if (auth_string) {
812e4bf2 263 ret = http_error("401", "Unauthorized",
72bd16db 264 "WWW-Authenticate: Basic realm=\""PNAME"\"\r\n",
5a830bf8 265 "\nYou must authenticate to view these pages.");
812e4bf2 266 } else {
267 ret = http_error("403", "Forbidden", NULL,
268 "This is a restricted-access set of pages.");
269 }
70322ae3 270 } else {
812e4bf2 271 p = ctx->url;
c47f39de 272 if (!html_parse_path(ctx->t, p, cfg, &index)) {
812e4bf2 273 ret = http_error("404", "Not Found", NULL,
c47f39de 274 "This is not a valid pathname.");
14601b5d 275 } else {
c47f39de 276 char *canonpath = html_format_path(ctx->t, cfg, index);
277 if (!strcmp(canonpath, p)) {
278 /*
279 * This is a canonical path. Return the document.
280 */
281 document = html_query(ctx->t, index, cfg, 1);
282 if (document) {
283 ret = http_success("text/html", 1, document);
284 sfree(document);
285 } else {
286 ret = http_error("404", "Not Found", NULL,
287 "This is not a valid pathname.");
288 }
289 } else {
290 /*
291 * This is a non-canonical path. Return a redirect
292 * to the right one.
293 *
294 * To do this, we must search the request headers
295 * for Host:, to see what the client thought it
296 * was calling our server.
297 */
298
299 char *host = NULL;
300 q = ctx->data + ctx->datalen;
301 for (p = ctx->headers; p < q; p++) {
302 const char *hdr = "Host:";
303 int i;
304 for (i = 0; hdr[i]; i++) {
305 if (p >= q || tolower((unsigned char)*p) !=
306 tolower((unsigned char)hdr[i]))
307 break;
308 p++;
309 }
310 if (!hdr[i])
311 break; /* found our header */
312 p = memchr(p, '\n', q - p);
313 if (!p)
314 p = q;
315 }
316 if (p < q) {
317 while (p < q && isspace((unsigned char)*p))
318 p++;
319 r = p;
320 while (p < q) {
321 if (*p == '\r' && (p+1 >= q || p[1] == '\n'))
322 break;
323 p++;
324 }
325 host = snewn(p-r+1, char);
326 memcpy(host, r, p-r);
327 host[p-r] = '\0';
328 }
329 if (host) {
330 char *header = dupfmt("Location: http://%s%s\r\n",
331 host, canonpath);
332 ret = http_error("301", "Moved", header,
333 "This is not the canonical form of"
334 " this pathname.");
335 sfree(header);
336 } else {
337 ret = http_error("400", "Bad Request", NULL,
338 "Needed a Host: header to return"
339 " the intended redirection.");
340 }
341 }
342 sfree(canonpath);
812e4bf2 343 }
70322ae3 344 }
812e4bf2 345 return ret;
346 } else
347 return NULL;
70322ae3 348}
349
350/* --- Platform support for running a web server. --- */
351
352enum { FD_CLIENT, FD_LISTENER, FD_CONNECTION };
353
354struct fd {
355 int fd;
356 int type;
357 int deleted;
358 char *wdata;
359 int wdatalen, wdatapos;
360 int magic_access;
361 struct connctx *cctx;
362};
363
364struct fd *fds = NULL;
365int nfds = 0, fdsize = 0;
366
367struct fd *new_fdstruct(int fd, int type)
368{
369 struct fd *ret;
370
371 if (nfds >= fdsize) {
372 fdsize = nfds * 3 / 2 + 32;
373 fds = sresize(fds, fdsize, struct fd);
374 }
375
376 ret = &fds[nfds++];
377
378 ret->fd = fd;
379 ret->type = type;
380 ret->wdata = NULL;
381 ret->wdatalen = ret->wdatapos = 0;
382 ret->cctx = NULL;
383 ret->deleted = 0;
384 ret->magic_access = 0;
385
386 return ret;
387}
388
812e4bf2 389int check_owning_uid(int fd, int flip)
70322ae3 390{
6f25b662 391 struct sockaddr_storage sock, peer;
392 int connected;
70322ae3 393 socklen_t addrlen;
6f25b662 394 char linebuf[4096], matchbuf[128];
395 char *filename;
396 int matchcol, uidcol;
70322ae3 397 FILE *fp;
398
399 addrlen = sizeof(sock);
812e4bf2 400 if (getsockname(fd, (struct sockaddr *)&sock, &addrlen)) {
70322ae3 401 fprintf(stderr, "getsockname: %s\n", strerror(errno));
402 exit(1);
403 }
404 addrlen = sizeof(peer);
6f25b662 405 connected = 1;
812e4bf2 406 if (getpeername(fd, (struct sockaddr *)&peer, &addrlen)) {
407 if (errno == ENOTCONN) {
6f25b662 408 connected = 0;
409 memset(&peer, 0, sizeof(peer));
410 peer.ss_family = sock.ss_family;
812e4bf2 411 } else {
412 fprintf(stderr, "getpeername: %s\n", strerror(errno));
413 exit(1);
414 }
415 }
416
417 if (flip) {
6f25b662 418 struct sockaddr_storage tmp = sock;
812e4bf2 419 sock = peer;
420 peer = tmp;
70322ae3 421 }
422
6f25b662 423#ifndef NO_IPV4
424 if (peer.ss_family == AF_INET) {
425 struct sockaddr_in *sock4 = (struct sockaddr_in *)&sock;
426 struct sockaddr_in *peer4 = (struct sockaddr_in *)&peer;
427
428 assert(peer4->sin_family == AF_INET);
429
430 sprintf(matchbuf, "%08X:%04X %08X:%04X",
431 peer4->sin_addr.s_addr, ntohs(peer4->sin_port),
432 sock4->sin_addr.s_addr, ntohs(sock4->sin_port));
433 filename = "/proc/net/tcp";
434 matchcol = 6;
435 uidcol = 75;
436 } else
437#endif
438#ifndef NO_IPV6
439 if (peer.ss_family == AF_INET6) {
440 struct sockaddr_in6 *sock6 = (struct sockaddr_in6 *)&sock;
441 struct sockaddr_in6 *peer6 = (struct sockaddr_in6 *)&peer;
442 char *p;
443
444 assert(peer6->sin6_family == AF_INET6);
445
446 p = matchbuf;
447 for (int i = 0; i < 4; i++)
448 p += sprintf(p, "%08X",
449 ((uint32_t *)peer6->sin6_addr.s6_addr)[i]);
450 p += sprintf(p, ":%04X ", ntohs(peer6->sin6_port));
451 for (int i = 0; i < 4; i++)
452 p += sprintf(p, "%08X",
453 ((uint32_t *)sock6->sin6_addr.s6_addr)[i]);
454 p += sprintf(p, ":%04X", ntohs(sock6->sin6_port));
455
456 filename = "/proc/net/tcp6";
457 matchcol = 6;
458 uidcol = 123;
459 } else
460#endif
461 {
462 return -1; /* unidentified family */
463 }
464
465 fp = fopen(filename, "r");
70322ae3 466 if (fp) {
467 while (fgets(linebuf, sizeof(linebuf), fp)) {
6f25b662 468 if (strlen(linebuf) >= uidcol &&
469 !strncmp(linebuf+matchcol, matchbuf, strlen(matchbuf))) {
14601b5d 470 fclose(fp);
6f25b662 471 return atoi(linebuf + uidcol);
70322ae3 472 }
473 }
14601b5d 474 fclose(fp);
70322ae3 475 }
812e4bf2 476
477 return -1;
478}
479
480void check_magic_access(struct fd *fd)
481{
482 if (check_owning_uid(fd->fd, 0) == getuid())
483 fd->magic_access = 1;
70322ae3 484}
485
812e4bf2 486static void base64_encode_atom(unsigned char *data, int n, char *out)
487{
488 static const char base64_chars[] =
489 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
490
491 unsigned word;
492
493 word = data[0] << 16;
494 if (n > 1)
495 word |= data[1] << 8;
496 if (n > 2)
497 word |= data[2];
498 out[0] = base64_chars[(word >> 18) & 0x3F];
499 out[1] = base64_chars[(word >> 12) & 0x3F];
500 if (n > 1)
501 out[2] = base64_chars[(word >> 6) & 0x3F];
502 else
503 out[2] = '=';
504 if (n > 2)
505 out[3] = base64_chars[word & 0x3F];
506 else
507 out[3] = '=';
508}
509
6f25b662 510struct listenfds {
511 int v4, v6;
512};
513
514static int make_listening_sockets(struct listenfds *fds, const char *address,
515 const char *portstr, char **outhostname)
70322ae3 516{
6f25b662 517 /*
518 * Establish up to 2 listening sockets, for IPv4 and IPv6, on the
519 * same arbitrarily selected port. Return them in fds.v4 and
520 * fds.v6, with each entry being -1 if that socket was not
521 * established at all. Main return value is the port chosen, or <0
522 * if the whole process failed.
523 */
524 struct sockaddr_in6 addr6;
525 struct sockaddr_in addr4;
526 int got_v6, got_v4;
70322ae3 527 socklen_t addrlen;
6f25b662 528 int ret, port = 0;
f2e52893 529
70322ae3 530 /*
6f25b662 531 * Special case of the address parameter: if it's "0.0.0.0", treat
532 * it like NULL, because that was how you specified listen-on-any-
533 * address in versions before the IPv6 revamp.
70322ae3 534 */
6f25b662 535 {
536 int u,v,w,x;
537 if (address &&
538 4 == sscanf(address, "%d.%d.%d.%d", &u, &v, &w, &x) &&
539 u==0 && v==0 && w==0 && x==0)
540 address = NULL;
70322ae3 541 }
6f25b662 542
543 if (portstr && !*portstr)
544 portstr = NULL; /* normalise NULL and empty string */
545
546 if (!address) {
547 char hostname[HOST_NAME_MAX];
548 if (gethostname(hostname, sizeof(hostname)) < 0) {
549 perror("hostname");
550 return -1;
551 }
552 *outhostname = dupstr(hostname);
1e8d78b9 553 } else {
6f25b662 554 *outhostname = dupstr(address);
1e8d78b9 555 }
6f25b662 556
557 fds->v6 = fds->v4 = -1;
558 got_v6 = got_v4 = 0;
559
560#if defined HAVE_GETADDRINFO
561
562 /*
563 * Resolve the given address using getaddrinfo, yielding an IPv6
564 * address or an IPv4 one or both.
565 */
566
567 struct addrinfo hints;
568 struct addrinfo *addrs, *ai;
569 hints.ai_family = AF_UNSPEC;
570 hints.ai_socktype = SOCK_STREAM;
571 hints.ai_protocol = 0;
572 hints.ai_flags = AI_PASSIVE;
712ecaa0 573 ret = getaddrinfo(address, portstr, &hints, &addrs);
6f25b662 574 if (ret) {
575 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
576 return -1;
15e73840 577 }
6f25b662 578 for (ai = addrs; ai; ai = ai->ai_next) {
579#ifndef NO_IPV6
580 if (!got_v6 && ai->ai_family == AF_INET6) {
581 memcpy(&addr6, ai->ai_addr, ai->ai_addrlen);
582 if (portstr && !port)
583 port = ntohs(addr6.sin6_port);
584 got_v6 = 1;
585 }
586#endif
587#ifndef NO_IPV4
588 if (!got_v4 && ai->ai_family == AF_INET) {
589 memcpy(&addr4, ai->ai_addr, ai->ai_addrlen);
590 if (portstr && !port)
591 port = ntohs(addr4.sin_port);
592 got_v4 = 1;
593 }
14557c01 594#endif
70322ae3 595 }
6f25b662 596
597#elif defined HAVE_GETHOSTBYNAME
598
599 /*
600 * IPv4-only setup using inet_addr and gethostbyname.
601 */
602 struct hostent *h;
603
604 memset(&addr4, 0, sizeof(addr4));
605 addr4.sin_family = AF_INET;
606
607 if (!address) {
608 addr4.sin_addr.s_addr = htons(INADDR_ANY);
609 got_v4 = 1;
610 } else if (inet_aton(address, &addr4.sin_addr)) {
611 got_v4 = 1; /* numeric address */
612 } else if ((h = gethostbyname(address)) != NULL) {
613 memcpy(&addr4.sin_addr, h->h_addr, sizeof(addr4.sin_addr));
614 got_v4 = 1;
615 } else {
616 fprintf(stderr, "gethostbyname: %s\n", hstrerror(h_errno));
617 return -1;
70322ae3 618 }
6f25b662 619
620 if (portstr) {
621 struct servent *s;
622 if (!portstr[strspn(portstr, "0123456789")]) {
623 port = atoi(portstr);
624 } else if ((s = getservbyname(portstr, NULL)) != NULL) {
625 port = ntohs(s->s_port);
626 } else {
627 fprintf(stderr, "getservbyname: port '%s' not understood\n",
628 portstr);
629 return -1;
630 }
70322ae3 631 }
6f25b662 632
633#endif
634
635#ifndef NO_IPV6
636#ifndef NO_IPV4
637 retry:
638#endif
639 if (got_v6) {
640 fds->v6 = socket(PF_INET6, SOCK_STREAM, 0);
641 if (fds->v6 < 0) {
642 fprintf(stderr, "socket(PF_INET6): %s\n", strerror(errno));
643 goto done_v6;
644 }
645#ifdef IPV6_V6ONLY
646 {
647 int i = 1;
648 if (setsockopt(fds->v6, IPPROTO_IPV6, IPV6_V6ONLY,
649 (char *)&i, sizeof(i)) < 0) {
650 fprintf(stderr, "setsockopt(IPV6_V6ONLY): %s\n",
651 strerror(errno));
652 close(fds->v6);
653 fds->v6 = -1;
654 goto done_v6;
655 }
656 }
657#endif /* IPV6_V6ONLY */
658 addr6.sin6_port = htons(port);
659 addrlen = sizeof(addr6);
660 if (bind(fds->v6, (const struct sockaddr *)&addr6, addrlen) < 0) {
661 fprintf(stderr, "bind: %s\n", strerror(errno));
662 close(fds->v6);
663 fds->v6 = -1;
664 goto done_v6;
665 }
666 if (listen(fds->v6, 5) < 0) {
667 fprintf(stderr, "listen: %s\n", strerror(errno));
668 close(fds->v6);
669 fds->v6 = -1;
670 goto done_v6;
671 }
672 if (port == 0) {
673 addrlen = sizeof(addr6);
674 if (getsockname(fds->v6, (struct sockaddr *)&addr6,
675 &addrlen) < 0) {
676 fprintf(stderr, "getsockname: %s\n", strerror(errno));
677 close(fds->v6);
678 fds->v6 = -1;
679 goto done_v6;
680 }
681 port = ntohs(addr6.sin6_port);
682 }
683 }
684 done_v6:
685#endif
686
687#ifndef NO_IPV4
688 if (got_v4) {
689 fds->v4 = socket(PF_INET, SOCK_STREAM, 0);
690 if (fds->v4 < 0) {
691 fprintf(stderr, "socket(PF_INET): %s\n", strerror(errno));
692 goto done_v4;
693 }
694 addr4.sin_port = htons(port);
695 addrlen = sizeof(addr4);
696 if (bind(fds->v4, (const struct sockaddr *)&addr4, addrlen) < 0) {
697#ifndef NO_IPV6
698 if (fds->v6 >= 0) {
699 /*
700 * If we support both v6 and v4, it's a failure
701 * condition if we didn't manage to bind to both. If
702 * the port number was arbitrary, we go round and try
703 * again. Otherwise, give up.
704 */
705 close(fds->v6);
706 close(fds->v4);
707 fds->v6 = fds->v4 = -1;
708 port = 0;
709 if (!portstr)
710 goto retry;
711 }
712#endif
713 fprintf(stderr, "bind: %s\n", strerror(errno));
714 close(fds->v4);
715 fds->v4 = -1;
716 goto done_v4;
717 }
718 if (listen(fds->v4, 5) < 0) {
719 fprintf(stderr, "listen: %s\n", strerror(errno));
720 close(fds->v4);
721 fds->v4 = -1;
722 goto done_v4;
723 }
724 if (port == 0) {
725 addrlen = sizeof(addr4);
726 if (getsockname(fds->v4, (struct sockaddr *)&addr4,
727 &addrlen) < 0) {
728 fprintf(stderr, "getsockname: %s\n", strerror(errno));
729 close(fds->v4);
730 fds->v4 = -1;
731 goto done_v4;
732 }
733 port = ntohs(addr4.sin_port);
734 }
735 }
736 done_v4:
737#endif
738
739 if (fds->v6 >= 0 || fds->v4 >= 0)
740 return port;
741 else
742 return -1;
743}
744
745void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg,
746 const struct html_config *incfg)
747{
748 struct listenfds lfds;
749 int ret, port;
750 int authtype;
751 char *authstring = NULL;
752 char *hostname;
753 struct sockaddr_in addr;
754 socklen_t addrlen;
755 struct html_config cfg = *incfg;
756
757 /*
758 * Establish the listening socket(s) and retrieve its port
759 * number.
760 */
761 port = make_listening_sockets(&lfds, dcfg->address, dcfg->port, &hostname);
762 if (port < 0)
763 exit(1); /* already reported an error */
764
812e4bf2 765 if ((authmask & HTTPD_AUTH_MAGIC) &&
6f25b662 766 (lfds.v4 < 0 || check_owning_uid(lfds.v4, 1) == getuid()) &&
767 (lfds.v6 < 0 || check_owning_uid(lfds.v6, 1) == getuid())) {
812e4bf2 768 authtype = HTTPD_AUTH_MAGIC;
1e8d78b9 769 if (authmask != HTTPD_AUTH_MAGIC)
770 printf("Using Linux /proc/net magic authentication\n");
812e4bf2 771 } else if ((authmask & HTTPD_AUTH_BASIC)) {
1e8d78b9 772 char username[128], password[128], userpassbuf[259];
773 const char *userpass;
812e4bf2 774 const char *rname;
775 unsigned char passbuf[10];
776 int i, j, k, fd;
777
778 authtype = HTTPD_AUTH_BASIC;
779
1e8d78b9 780 if (authmask != HTTPD_AUTH_BASIC)
781 printf("Using HTTP Basic authentication\n");
782
783 if (dcfg->basicauthdata) {
784 userpass = dcfg->basicauthdata;
785 } else {
bf53e756 786 strcpy(username, PNAME);
1e8d78b9 787 rname = "/dev/urandom";
812e4bf2 788 fd = open(rname, O_RDONLY);
789 if (fd < 0) {
1e8d78b9 790 int err = errno;
791 rname = "/dev/random";
792 fd = open(rname, O_RDONLY);
793 if (fd < 0) {
794 int err2 = errno;
795 fprintf(stderr, "/dev/urandom: open: %s\n", strerror(err));
796 fprintf(stderr, "/dev/random: open: %s\n", strerror(err2));
797 exit(1);
798 }
812e4bf2 799 }
1e8d78b9 800 for (i = 0; i < 10 ;) {
801 j = read(fd, passbuf + i, 10 - i);
802 if (j <= 0) {
803 fprintf(stderr, "%s: read: %s\n", rname,
804 j < 0 ? strerror(errno) : "unexpected EOF");
805 exit(1);
806 }
807 i += j;
812e4bf2 808 }
1e8d78b9 809 close(fd);
810 for (i = 0; i < 16; i++) {
811 /*
812 * 32 characters out of the 36 alphanumerics gives
813 * me the latitude to discard i,l,o for being too
814 * numeric-looking, and w because it has two too
815 * many syllables and one too many presidential
816 * associations.
817 */
818 static const char chars[32] =
819 "0123456789abcdefghjkmnpqrstuvxyz";
820 int v = 0;
821
822 k = i / 8 * 5;
823 for (j = 0; j < 5; j++)
824 v |= ((passbuf[k+j] >> (i%8)) & 1) << j;
825
826 password[i] = chars[v];
827 }
828 password[i] = '\0';
829
830 sprintf(userpassbuf, "%s:%s", username, password);
831 userpass = userpassbuf;
812e4bf2 832
1e8d78b9 833 printf("Username: %s\nPassword: %s\n", username, password);
834 }
812e4bf2 835
1e8d78b9 836 k = strlen(userpass);
837 authstring = snewn(k * 4 / 3 + 16, char);
812e4bf2 838 for (i = j = 0; i < k ;) {
839 int s = k-i < 3 ? k-i : 3;
1e8d78b9 840 base64_encode_atom((unsigned char *)(userpass+i), s, authstring+j);
812e4bf2 841 i += s;
842 j += 4;
843 }
1e8d78b9 844 authstring[j] = '\0';
845 } else if ((authmask & HTTPD_AUTH_NONE)) {
812e4bf2 846 authtype = HTTPD_AUTH_NONE;
1e8d78b9 847 if (authmask != HTTPD_AUTH_NONE)
848 printf("Web server is unauthenticated\n");
849 } else {
bf53e756 850 fprintf(stderr, PNAME ": authentication method not supported\n");
1e8d78b9 851 exit(1);
852 }
6f25b662 853 if (port == 80) {
854 printf("URL: http://%s/\n", hostname);
15e73840 855 } else {
6f25b662 856 printf("URL: http://%s:%d/\n", hostname, port);
812e4bf2 857 }
a8a4d6d8 858 fflush(stdout);
70322ae3 859
860 /*
6f25b662 861 * Now construct fd structure(s) to hold the listening sockets.
70322ae3 862 */
6f25b662 863 if (lfds.v4 >= 0)
864 new_fdstruct(lfds.v4, FD_LISTENER);
865 if (lfds.v6 >= 0)
866 new_fdstruct(lfds.v6, FD_LISTENER);
70322ae3 867
a8a4d6d8 868 if (dcfg->closeoneof) {
869 /*
870 * Read from standard input, and treat EOF as a notification
871 * to exit.
872 */
873 new_fdstruct(0, FD_CLIENT);
874 }
70322ae3 875
876 /*
877 * Now we're ready to run our main loop. Keep looping round on
878 * select.
879 */
880 while (1) {
881 fd_set rfds, wfds;
50e82fdc 882 int i, j;
883 SELECT_TYPE_ARG1 maxfd;
884 int ret;
70322ae3 885
886#define FD_SET_MAX(fd, set, max) \
887 do { FD_SET((fd),(set)); (max) = ((max)<=(fd)?(fd)+1:(max)); } while(0)
888
889 /*
890 * Loop round the fd list putting fds into our select
891 * sets. Also in this loop we remove any that were marked
892 * as deleted in the previous loop.
893 */
894 FD_ZERO(&rfds);
895 FD_ZERO(&wfds);
896 maxfd = 0;
897 for (i = j = 0; j < nfds; j++) {
898
899 if (fds[j].deleted) {
900 sfree(fds[j].wdata);
901 free_connection(fds[j].cctx);
902 continue;
903 }
904 fds[i] = fds[j];
905
906 switch (fds[i].type) {
907 case FD_CLIENT:
15e73840 908 FD_SET_MAX(fds[i].fd, &rfds, maxfd);
909 break;
70322ae3 910 case FD_LISTENER:
911 FD_SET_MAX(fds[i].fd, &rfds, maxfd);
912 break;
913 case FD_CONNECTION:
914 /*
915 * Always read from a connection socket. Even
916 * after we've started writing, the peer might
917 * still be sending (e.g. because we shamefully
918 * jumped the gun before waiting for the end of
919 * the HTTP request) and so we should be prepared
920 * to read data and throw it away.
921 */
922 FD_SET_MAX(fds[i].fd, &rfds, maxfd);
923 /*
924 * Also attempt to write, if we have data to write.
925 */
926 if (fds[i].wdatapos < fds[i].wdatalen)
927 FD_SET_MAX(fds[i].fd, &wfds, maxfd);
928 break;
929 }
930
931 i++;
932 }
933 nfds = i;
934
50e82fdc 935 ret = select(maxfd, SELECT_TYPE_ARG234 &rfds,
936 SELECT_TYPE_ARG234 &wfds, SELECT_TYPE_ARG234 NULL,
937 SELECT_TYPE_ARG5 NULL);
70322ae3 938 if (ret <= 0) {
939 if (ret < 0 && (errno != EINTR)) {
940 fprintf(stderr, "select: %s", strerror(errno));
941 exit(1);
942 }
943 continue;
944 }
945
946 for (i = 0; i < nfds; i++) {
947 switch (fds[i].type) {
948 case FD_CLIENT:
949 if (FD_ISSET(fds[i].fd, &rfds)) {
950 char buf[4096];
951 int ret = read(fds[i].fd, buf, sizeof(buf));
952 if (ret <= 0) {
953 if (ret < 0) {
954 fprintf(stderr, "standard input: read: %s\n",
955 strerror(errno));
956 exit(1);
957 }
958 return;
959 }
960 }
961 break;
962 case FD_LISTENER:
963 if (FD_ISSET(fds[i].fd, &rfds)) {
964 /*
965 * New connection has come in. Accept it.
966 */
967 struct fd *f;
968 struct sockaddr_in addr;
969 socklen_t addrlen = sizeof(addr);
970 int newfd = accept(fds[i].fd, (struct sockaddr *)&addr,
971 &addrlen);
972 if (newfd < 0)
973 break; /* not sure what happened there */
974
975 f = new_fdstruct(newfd, FD_CONNECTION);
976 f->cctx = new_connection(t);
812e4bf2 977 if (authtype == HTTPD_AUTH_MAGIC)
978 check_magic_access(f);
70322ae3 979 }
980 break;
981 case FD_CONNECTION:
982 if (FD_ISSET(fds[i].fd, &rfds)) {
983 /*
984 * There's data to be read.
985 */
986 char readbuf[4096];
987 int ret;
988
989 ret = read(fds[i].fd, readbuf, sizeof(readbuf));
990 if (ret <= 0) {
991 /*
992 * This shouldn't happen in a sensible
993 * HTTP connection, so we abandon the
994 * connection if it does.
995 */
996 close(fds[i].fd);
997 fds[i].deleted = 1;
998 break;
999 } else {
1000 if (!fds[i].wdata) {
1001 /*
1002 * If we haven't got an HTTP response
1003 * yet, keep processing data in the
1004 * hope of acquiring one.
1005 */
812e4bf2 1006 fds[i].wdata = got_data
1007 (fds[i].cctx, readbuf, ret,
1008 (authtype == HTTPD_AUTH_NONE ||
f2e52893 1009 fds[i].magic_access), authstring, &cfg);
70322ae3 1010 if (fds[i].wdata) {
1011 fds[i].wdatalen = strlen(fds[i].wdata);
1012 fds[i].wdatapos = 0;
1013 }
1014 } else {
1015 /*
1016 * Otherwise, just drop our read data
1017 * on the floor.
1018 */
1019 }
1020 }
1021 }
1022 if (FD_ISSET(fds[i].fd, &wfds) &&
1023 fds[i].wdatapos < fds[i].wdatalen) {
1024 /*
1025 * The socket is writable, and we have data to
1026 * write. Write it.
1027 */
1028 int ret = write(fds[i].fd, fds[i].wdata + fds[i].wdatapos,
1029 fds[i].wdatalen - fds[i].wdatapos);
1030 if (ret <= 0) {
1031 /*
1032 * Shouldn't happen; abandon the connection.
1033 */
1034 close(fds[i].fd);
1035 fds[i].deleted = 1;
1036 break;
1037 } else {
1038 fds[i].wdatapos += ret;
1039 if (fds[i].wdatapos == fds[i].wdatalen) {
1040 shutdown(fds[i].fd, SHUT_WR);
1041 }
1042 }
1043 }
1044 break;
1045 }
1046 }
1047
1048 }
1049}