2 * httpd.c: implementation of httpd.h.
10 /* --- Logic driving what the web server's responses are. --- */
12 enum { /* connctx states */
21 int datalen
, datasize
;
22 char *method
, *url
, *headers
, *auth
;
27 * Called when a new connection arrives on a listening socket.
28 * Returns a connctx for the new connection.
30 struct connctx
*new_connection(const void *t
)
32 struct connctx
*cctx
= snew(struct connctx
);
35 cctx
->datalen
= cctx
->datasize
= 0;
36 cctx
->state
= READING_REQ_LINE
;
37 cctx
->method
= cctx
->url
= cctx
->headers
= cctx
->auth
= NULL
;
41 void free_connection(struct connctx
*cctx
)
47 static char *http_error(char *code
, char *errmsg
, char *extraheader
,
50 return dupfmt("HTTP/1.1 %s %s\r\n"
52 "Server: " PNAME
"\r\n"
53 "Connection: close\r\n"
55 "Content-Type: text/html; charset=US-ASCII\r\n"
57 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
59 "<TITLE>%s %s</TITLE>\r\n"
63 "</BODY></HTML>\r\n", code
, errmsg
,
64 extraheader ? extraheader
: "",
65 code
, errmsg
, code
, errmsg
, errtext
);
68 static char *http_success(char *mimetype
, int stuff_cr
, char *document
)
70 return dupfmt("HTTP/1.1 200 OK\r\n"
73 "Server: " PNAME
"\r\n"
74 "Connection: close\r\n"
75 "Content-Type: %s\r\n"
77 "%S", mimetype
, stuff_cr
, document
);
81 * Called when data comes in on a connection.
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.
88 char *got_data(struct connctx
*ctx
, char *data
, int length
,
89 int magic_access
, const char *auth_string
,
90 const struct html_config
*cfg
)
92 char *line
, *p
, *q
, *r
, *z1
, *z2
, c1
, c2
;
98 * Add the data we've just received to our buffer.
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);
104 memcpy(ctx
->data
+ ctx
->datalen
, data
, length
);
105 ctx
->datalen
+= length
;
108 * Gradually process the HTTP request as we receive it.
110 if (ctx
->state
== READING_REQ_LINE
) {
112 * We're waiting for the first line of the input, which
113 * contains the main HTTP request. See if we've got it
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.'
124 while (line
- ctx
->data
< ctx
->datalen
&&
125 (*line
== '\r' || *line
== '\n'))
128 while (q
- ctx
->data
< ctx
->datalen
&& *q
!= '\n')
130 if (q
- ctx
->data
>= ctx
->datalen
)
131 return NULL
; /* not got request line yet */
134 * We've got the first line of the request. Zero-terminate
135 * and parse it into method, URL and optional HTTP
140 if (q
> line
&& q
[-1] == '\r')
145 while (*p
&& !isspace((unsigned char)*p
)) p
++;
151 while (*p
&& isspace((unsigned char)*p
)) p
++;
153 while (*q
&& !isspace((unsigned char)*q
)) q
++;
157 while (*q
&& isspace((unsigned char)*q
)) q
++;
160 * Now `line' points at the method name; p points at the
161 * URL, if any; q points at the HTTP version, if any.
165 * There should _be_ a URL, on any request type at all.
169 /* Restore the request to the way we received it. */
172 text
= dupfmt("<code>" PNAME
"</code> received the HTTP request"
173 " \"<code>%h</code>\", which contains no URL.",
175 ret
= http_error("400", "Bad request", NULL
, text
);
184 * If there was an HTTP version, we might need to see
185 * headers. Otherwise, the request is done.
188 ctx
->state
= READING_HEADERS
;
194 if (ctx
->state
== READING_HEADERS
) {
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.
200 q
= ctx
->data
+ ctx
->datalen
;
201 for (p
= ctx
->headers
; p
< q
; p
++) {
203 ((p
+1 < q
&& p
[1] == '\n') ||
204 (p
+2 < q
&& p
[1] == '\r' && p
[2] == '\n'))) {
212 if (ctx
->state
== DONE
) {
214 * Now we have the entire HTTP request. Decide what to do
219 * Search the request headers for Authorization.
221 q
= ctx
->data
+ ctx
->datalen
;
222 for (p
= ctx
->headers
; p
< q
; p
++) {
223 const char *hdr
= "Authorization:";
225 for (i
= 0; hdr
[i
]; i
++) {
226 if (p
>= q
|| tolower((unsigned char)*p
) !=
227 tolower((unsigned char)hdr
[i
]))
232 break; /* found our header */
233 p
= memchr(p
, '\n', q
- p
);
238 while (p
< q
&& isspace((unsigned char)*p
))
241 while (p
< q
&& !isspace((unsigned char)*p
))
245 if (!strcasecmp(r
, "Basic")) {
246 while (p
< q
&& isspace((unsigned char)*p
))
249 while (p
< q
&& !isspace((unsigned char)*p
))
253 if (!strcmp(r
, auth_string
))
261 if (!magic_access
&& !auth_correct
) {
263 ret
= http_error("401", "Unauthorized",
264 "WWW-Authenticate: Basic realm=\""PNAME
"\"\r\n",
265 "\nYou must authenticate to view these pages.");
267 ret
= http_error("403", "Forbidden", NULL
,
268 "This is a restricted-access set of pages.");
272 if (!html_parse_path(ctx
->t
, p
, cfg
, &index
)) {
273 ret
= http_error("404", "Not Found", NULL
,
274 "This is not a valid pathname.");
276 char *canonpath
= html_format_path(ctx
->t
, cfg
, index
);
277 if (!strcmp(canonpath
, p
)) {
279 * This is a canonical path. Return the document.
281 document
= html_query(ctx
->t
, index
, cfg
, 1);
283 ret
= http_success("text/html", 1, document
);
286 ret
= http_error("404", "Not Found", NULL
,
287 "This is not a valid pathname.");
291 * This is a non-canonical path. Return a redirect
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.
300 q
= ctx
->data
+ ctx
->datalen
;
301 for (p
= ctx
->headers
; p
< q
; p
++) {
302 const char *hdr
= "Host:";
304 for (i
= 0; hdr
[i
]; i
++) {
305 if (p
>= q
|| tolower((unsigned char)*p
) !=
306 tolower((unsigned char)hdr
[i
]))
311 break; /* found our header */
312 p
= memchr(p
, '\n', q
- p
);
317 while (p
< q
&& isspace((unsigned char)*p
))
321 if (*p
== '\r' && (p
+1 >= q
|| p
[1] == '\n'))
325 host
= snewn(p
-r
+1, char);
326 memcpy(host
, r
, p
-r
);
330 char *header
= dupfmt("Location: http://%s%s\r\n",
332 ret
= http_error("301", "Moved", header
,
333 "This is not the canonical form of"
337 ret
= http_error("400", "Bad Request", NULL
,
338 "Needed a Host: header to return"
339 " the intended redirection.");
350 /* --- Platform support for running a web server. --- */
352 enum { FD_CLIENT
, FD_LISTENER
, FD_CONNECTION
};
359 int wdatalen
, wdatapos
;
361 struct connctx
*cctx
;
364 struct fd
*fds
= NULL
;
365 int nfds
= 0, fdsize
= 0;
367 struct fd
*new_fdstruct(int fd
, int type
)
371 if (nfds
>= fdsize
) {
372 fdsize
= nfds
* 3 / 2 + 32;
373 fds
= sresize(fds
, fdsize
, struct fd
);
381 ret
->wdatalen
= ret
->wdatapos
= 0;
384 ret
->magic_access
= 0;
389 int check_owning_uid(int fd
, int flip
)
391 struct sockaddr_in sock
, peer
;
393 char linebuf
[4096], matchbuf
[80];
396 addrlen
= sizeof(sock
);
397 if (getsockname(fd
, (struct sockaddr
*)&sock
, &addrlen
)) {
398 fprintf(stderr
, "getsockname: %s\n", strerror(errno
));
401 addrlen
= sizeof(peer
);
402 if (getpeername(fd
, (struct sockaddr
*)&peer
, &addrlen
)) {
403 if (errno
== ENOTCONN
) {
404 peer
.sin_addr
.s_addr
= htonl(0);
405 peer
.sin_port
= htons(0);
407 fprintf(stderr
, "getpeername: %s\n", strerror(errno
));
413 struct sockaddr_in tmp
= sock
;
418 sprintf(matchbuf
, "%08X:%04X %08X:%04X",
419 peer
.sin_addr
.s_addr
, ntohs(peer
.sin_port
),
420 sock
.sin_addr
.s_addr
, ntohs(sock
.sin_port
));
421 fp
= fopen("/proc/net/tcp", "r");
423 while (fgets(linebuf
, sizeof(linebuf
), fp
)) {
424 if (strlen(linebuf
) >= 75 &&
425 !strncmp(linebuf
+6, matchbuf
, strlen(matchbuf
))) {
427 return atoi(linebuf
+ 75);
436 void check_magic_access(struct fd
*fd
)
438 if (check_owning_uid(fd
->fd
, 0) == getuid())
439 fd
->magic_access
= 1;
442 static void base64_encode_atom(unsigned char *data
, int n
, char *out
)
444 static const char base64_chars
[] =
445 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
449 word
= data
[0] << 16;
451 word
|= data
[1] << 8;
454 out
[0] = base64_chars
[(word
>> 18) & 0x3F];
455 out
[1] = base64_chars
[(word
>> 12) & 0x3F];
457 out
[2] = base64_chars
[(word
>> 6) & 0x3F];
461 out
[3] = base64_chars
[word
& 0x3F];
466 void run_httpd(const void *t
, int authmask
, const struct httpd_config
*dcfg
,
467 const struct html_config
*incfg
)
471 char *authstring
= NULL
;
472 struct sockaddr_in addr
;
474 struct html_config cfg
= *incfg
;
477 * Establish the listening socket and retrieve its port
480 fd
= socket(PF_INET
, SOCK_STREAM
, 0);
482 fprintf(stderr
, "socket(PF_INET): %s\n", strerror(errno
));
485 memset(&addr
, 0, sizeof(addr
));
486 addr
.sin_family
= AF_INET
;
487 if (!dcfg
->address
) {
488 #ifdef RANDOM_LOCALHOST
489 unsigned long ipaddr
;
492 ipaddr
+= (1 + rand() % 255) << 16;
493 ipaddr
+= (1 + rand() % 255) << 8;
494 ipaddr
+= (1 + rand() % 255);
495 addr
.sin_addr
.s_addr
= htonl(ipaddr
);
497 addr
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
499 addr
.sin_port
= htons(0);
501 addr
.sin_addr
.s_addr
= inet_addr(dcfg
->address
);
502 addr
.sin_port
= dcfg
->port ?
htons(dcfg
->port
) : 0;
504 addrlen
= sizeof(addr
);
505 ret
= bind(fd
, (const struct sockaddr
*)&addr
, addrlen
);
506 #ifdef RANDOM_LOCALHOST
507 if (ret
< 0 && errno
== EADDRNOTAVAIL
&& !dcfg
->address
) {
509 * Some systems don't like us binding to random weird
510 * localhost-space addresses. Try again with the official
513 addr
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
514 addr
.sin_port
= htons(0);
515 ret
= bind(fd
, (const struct sockaddr
*)&addr
, addrlen
);
519 fprintf(stderr
, "bind: %s\n", strerror(errno
));
522 if (listen(fd
, 5) < 0) {
523 fprintf(stderr
, "listen: %s\n", strerror(errno
));
526 addrlen
= sizeof(addr
);
527 if (getsockname(fd
, (struct sockaddr
*)&addr
, &addrlen
)) {
528 fprintf(stderr
, "getsockname: %s\n", strerror(errno
));
531 if ((authmask
& HTTPD_AUTH_MAGIC
) &&
532 (check_owning_uid(fd
, 1) == getuid())) {
533 authtype
= HTTPD_AUTH_MAGIC
;
534 if (authmask
!= HTTPD_AUTH_MAGIC
)
535 printf("Using Linux /proc/net magic authentication\n");
536 } else if ((authmask
& HTTPD_AUTH_BASIC
)) {
537 char username
[128], password
[128], userpassbuf
[259];
538 const char *userpass
;
540 unsigned char passbuf
[10];
543 authtype
= HTTPD_AUTH_BASIC
;
545 if (authmask
!= HTTPD_AUTH_BASIC
)
546 printf("Using HTTP Basic authentication\n");
548 if (dcfg
->basicauthdata
) {
549 userpass
= dcfg
->basicauthdata
;
551 strcpy(username
, PNAME
);
552 rname
= "/dev/urandom";
553 fd
= open(rname
, O_RDONLY
);
556 rname
= "/dev/random";
557 fd
= open(rname
, O_RDONLY
);
560 fprintf(stderr
, "/dev/urandom: open: %s\n", strerror(err
));
561 fprintf(stderr
, "/dev/random: open: %s\n", strerror(err2
));
565 for (i
= 0; i
< 10 ;) {
566 j
= read(fd
, passbuf
+ i
, 10 - i
);
568 fprintf(stderr
, "%s: read: %s\n", rname
,
569 j
< 0 ?
strerror(errno
) : "unexpected EOF");
575 for (i
= 0; i
< 16; i
++) {
577 * 32 characters out of the 36 alphanumerics gives
578 * me the latitude to discard i,l,o for being too
579 * numeric-looking, and w because it has two too
580 * many syllables and one too many presidential
583 static const char chars
[32] =
584 "0123456789abcdefghjkmnpqrstuvxyz";
588 for (j
= 0; j
< 5; j
++)
589 v
|= ((passbuf
[k
+j
] >> (i
%8)) & 1) << j
;
591 password
[i
] = chars
[v
];
595 sprintf(userpassbuf
, "%s:%s", username
, password
);
596 userpass
= userpassbuf
;
598 printf("Username: %s\nPassword: %s\n", username
, password
);
601 k
= strlen(userpass
);
602 authstring
= snewn(k
* 4 / 3 + 16, char);
603 for (i
= j
= 0; i
< k
;) {
604 int s
= k
-i
< 3 ? k
-i
: 3;
605 base64_encode_atom((unsigned char *)(userpass
+i
), s
, authstring
+j
);
609 authstring
[j
] = '\0';
610 } else if ((authmask
& HTTPD_AUTH_NONE
)) {
611 authtype
= HTTPD_AUTH_NONE
;
612 if (authmask
!= HTTPD_AUTH_NONE
)
613 printf("Web server is unauthenticated\n");
615 fprintf(stderr
, PNAME
": authentication method not supported\n");
618 if (ntohs(addr
.sin_addr
.s_addr
) == INADDR_ANY
) {
619 printf("Server port: %d\n", ntohs(addr
.sin_port
));
620 } else if (ntohs(addr
.sin_port
) == 80) {
621 printf("URL: http://%s/\n", inet_ntoa(addr
.sin_addr
));
623 printf("URL: http://%s:%d/\n",
624 inet_ntoa(addr
.sin_addr
), ntohs(addr
.sin_port
));
629 * Now construct an fd structure to hold it.
631 new_fdstruct(fd
, FD_LISTENER
);
633 if (dcfg
->closeoneof
) {
635 * Read from standard input, and treat EOF as a notification
638 new_fdstruct(0, FD_CLIENT
);
642 * Now we're ready to run our main loop. Keep looping round on
648 SELECT_TYPE_ARG1 maxfd
;
651 #define FD_SET_MAX(fd, set, max) \
652 do { FD_SET((fd),(set)); (max) = ((max)<=(fd)?(fd)+1:(max)); } while(0)
655 * Loop round the fd list putting fds into our select
656 * sets. Also in this loop we remove any that were marked
657 * as deleted in the previous loop.
662 for (i
= j
= 0; j
< nfds
; j
++) {
664 if (fds
[j
].deleted
) {
666 free_connection(fds
[j
].cctx
);
671 switch (fds
[i
].type
) {
673 FD_SET_MAX(fds
[i
].fd
, &rfds
, maxfd
);
676 FD_SET_MAX(fds
[i
].fd
, &rfds
, maxfd
);
680 * Always read from a connection socket. Even
681 * after we've started writing, the peer might
682 * still be sending (e.g. because we shamefully
683 * jumped the gun before waiting for the end of
684 * the HTTP request) and so we should be prepared
685 * to read data and throw it away.
687 FD_SET_MAX(fds
[i
].fd
, &rfds
, maxfd
);
689 * Also attempt to write, if we have data to write.
691 if (fds
[i
].wdatapos
< fds
[i
].wdatalen
)
692 FD_SET_MAX(fds
[i
].fd
, &wfds
, maxfd
);
700 ret
= select(maxfd
, SELECT_TYPE_ARG234
&rfds
,
701 SELECT_TYPE_ARG234
&wfds
, SELECT_TYPE_ARG234 NULL
,
702 SELECT_TYPE_ARG5 NULL
);
704 if (ret
< 0 && (errno
!= EINTR
)) {
705 fprintf(stderr
, "select: %s", strerror(errno
));
711 for (i
= 0; i
< nfds
; i
++) {
712 switch (fds
[i
].type
) {
714 if (FD_ISSET(fds
[i
].fd
, &rfds
)) {
716 int ret
= read(fds
[i
].fd
, buf
, sizeof(buf
));
719 fprintf(stderr
, "standard input: read: %s\n",
728 if (FD_ISSET(fds
[i
].fd
, &rfds
)) {
730 * New connection has come in. Accept it.
733 struct sockaddr_in addr
;
734 socklen_t addrlen
= sizeof(addr
);
735 int newfd
= accept(fds
[i
].fd
, (struct sockaddr
*)&addr
,
738 break; /* not sure what happened there */
740 f
= new_fdstruct(newfd
, FD_CONNECTION
);
741 f
->cctx
= new_connection(t
);
742 if (authtype
== HTTPD_AUTH_MAGIC
)
743 check_magic_access(f
);
747 if (FD_ISSET(fds
[i
].fd
, &rfds
)) {
749 * There's data to be read.
754 ret
= read(fds
[i
].fd
, readbuf
, sizeof(readbuf
));
757 * This shouldn't happen in a sensible
758 * HTTP connection, so we abandon the
759 * connection if it does.
767 * If we haven't got an HTTP response
768 * yet, keep processing data in the
769 * hope of acquiring one.
771 fds
[i
].wdata
= got_data
772 (fds
[i
].cctx
, readbuf
, ret
,
773 (authtype
== HTTPD_AUTH_NONE
||
774 fds
[i
].magic_access
), authstring
, &cfg
);
776 fds
[i
].wdatalen
= strlen(fds
[i
].wdata
);
781 * Otherwise, just drop our read data
787 if (FD_ISSET(fds
[i
].fd
, &wfds
) &&
788 fds
[i
].wdatapos
< fds
[i
].wdatalen
) {
790 * The socket is writable, and we have data to
793 int ret
= write(fds
[i
].fd
, fds
[i
].wdata
+ fds
[i
].wdatapos
,
794 fds
[i
].wdatalen
- fds
[i
].wdatapos
);
797 * Shouldn't happen; abandon the connection.
803 fds
[i
].wdatapos
+= ret
;
804 if (fds
[i
].wdatapos
== fds
[i
].wdatalen
) {
805 shutdown(fds
[i
].fd
, SHUT_WR
);