2 * httpd.c: implementation of httpd.h.
15 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <arpa/inet.h>
20 #include <netinet/in.h>
26 /* --- Logic driving what the web server's responses are. --- */
31 int datalen
, datasize
;
35 * Called when a new connection arrives on a listening socket.
36 * Returns a connctx for the new connection.
38 struct connctx
*new_connection(const void *t
)
40 struct connctx
*cctx
= snew(struct connctx
);
43 cctx
->datalen
= cctx
->datasize
= 0;
47 void free_connection(struct connctx
*cctx
)
53 static char *http_error(char *code
, char *errmsg
, char *errtext
, ...)
55 return dupfmt("HTTP/1.1 %s %s\r\n"
58 "Connection: close\r\n"
59 "Content-Type: text/html; charset=US-ASCII\r\n"
61 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
63 "<TITLE>%s %s</TITLE>\r\n"
67 "</BODY></HTML>\r\n", code
, errmsg
,
68 code
, errmsg
, code
, errmsg
, errtext
);
71 static char *http_success(char *mimetype
, int stuff_cr
, char *document
)
73 return dupfmt("HTTP/1.1 200 OK\r\n"
77 "Connection: close\r\n"
78 "Content-Type: %s\r\n"
80 "%S", mimetype
, stuff_cr
, document
);
84 * Called when data comes in on a connection.
86 * If this function returns NULL, the platform code continues
87 * reading from the socket. Otherwise, it returns some dynamically
88 * allocated data which the platform code will then write to the
89 * socket before closing it.
91 char *got_data(struct connctx
*ctx
, char *data
, int length
, int magic_access
)
93 char *line
, *p
, *q
, *z1
, *z2
, c1
, c2
;
97 if (ctx
->datasize
< ctx
->datalen
+ length
) {
98 ctx
->datasize
= (ctx
->datalen
+ length
) * 3 / 2 + 4096;
99 ctx
->data
= sresize(ctx
->data
, ctx
->datasize
, char);
101 memcpy(ctx
->data
+ ctx
->datalen
, data
, length
);
102 ctx
->datalen
+= length
;
105 * See if we have enough of an HTTP request to work out our
110 * RFC 2616 section 4.1: `In the interest of robustness, [...]
111 * if the server is reading the protocol stream at the
112 * beginning of a message and receives a CRLF first, it should
115 while (line
- ctx
->data
< ctx
->datalen
&&
116 (*line
== '\r' || *line
== '\n'))
120 while (q
- ctx
->data
< ctx
->datalen
&& *q
!= '\r' && *q
!= '\n')
122 if (q
- ctx
->data
>= ctx
->datalen
)
123 return NULL
; /* not got request line yet */
126 * We've got the first line of the request, which is enough
127 * for us to work out what to say in response and start
128 * sending it. The platform side will keep reading data, but
131 * So, zero-terminate our line and parse it.
137 while (*p
&& !isspace((unsigned char)*p
)) p
++;
143 while (*p
&& isspace((unsigned char)*p
)) p
++;
145 while (*q
&& !isspace((unsigned char)*q
)) q
++;
151 * Now `line' points at the method name; p points at the URL,
156 * There should _be_ a URL, on any request type at all.
162 text
= dupfmt("<code>agedu</code> received the HTTP request"
163 " \"<code>%s</code>\", which contains no URL.",
165 ret
= http_error("400", "Bad request", text
);
171 ret
= http_error("403", "Forbidden",
172 "This is a restricted-access set of pages.");
174 p
+= strspn(p
, "/?");
175 index
= strtoul(p
, NULL
, 10);
176 document
= html_query(ctx
->t
, index
, "%lu");
178 ret
= http_success("text/html", 1, document
);
181 ret
= http_error("404", "Not Found",
182 "Pathname index out of range.");
188 /* --- Platform support for running a web server. --- */
190 enum { FD_CLIENT
, FD_LISTENER
, FD_CONNECTION
};
197 int wdatalen
, wdatapos
;
199 struct connctx
*cctx
;
202 struct fd
*fds
= NULL
;
203 int nfds
= 0, fdsize
= 0;
205 struct fd
*new_fdstruct(int fd
, int type
)
209 if (nfds
>= fdsize
) {
210 fdsize
= nfds
* 3 / 2 + 32;
211 fds
= sresize(fds
, fdsize
, struct fd
);
219 ret
->wdatalen
= ret
->wdatapos
= 0;
222 ret
->magic_access
= 0;
227 void check_magic_access(struct fd
*fd
)
229 struct sockaddr_in sock
, peer
;
231 char linebuf
[4096], matchbuf
[80];
234 addrlen
= sizeof(sock
);
235 if (getsockname(fd
->fd
, (struct sockaddr
*)&sock
, &addrlen
)) {
236 fprintf(stderr
, "getsockname: %s\n", strerror(errno
));
239 addrlen
= sizeof(peer
);
240 if (getpeername(fd
->fd
, (struct sockaddr
*)&peer
, &addrlen
)) {
241 fprintf(stderr
, "getpeername: %s\n", strerror(errno
));
245 sprintf(matchbuf
, "%08X:%04X %08X:%04X",
246 peer
.sin_addr
.s_addr
, ntohs(peer
.sin_port
),
247 sock
.sin_addr
.s_addr
, ntohs(sock
.sin_port
));
248 fp
= fopen("/proc/net/tcp", "r");
250 while (fgets(linebuf
, sizeof(linebuf
), fp
)) {
251 if (strlen(linebuf
) >= 75 &&
252 !strncmp(linebuf
+6, matchbuf
, strlen(matchbuf
))) {
253 int uid
= atoi(linebuf
+ 75);
255 fd
->magic_access
= 1;
261 void run_httpd(const void *t
)
264 unsigned long ipaddr
;
266 struct sockaddr_in addr
;
270 * Establish the listening socket and retrieve its port
273 fd
= socket(PF_INET
, SOCK_STREAM
, 0);
275 fprintf(stderr
, "socket(PF_INET): %s\n", strerror(errno
));
278 addr
.sin_family
= AF_INET
;
281 ipaddr
+= (1 + rand() % 255) << 16;
282 ipaddr
+= (1 + rand() % 255) << 8;
283 ipaddr
+= (1 + rand() % 255);
284 addr
.sin_addr
.s_addr
= htonl(ipaddr
);
285 addr
.sin_port
= htons(0);
286 addrlen
= sizeof(addr
);
287 if (bind(fd
, (struct sockaddr
*)&addr
, addrlen
) < 0) {
288 fprintf(stderr
, "bind: %s\n", strerror(errno
));
291 if (listen(fd
, 5) < 0) {
292 fprintf(stderr
, "listen: %s\n", strerror(errno
));
295 addrlen
= sizeof(addr
);
296 if (getsockname(fd
, (struct sockaddr
*)&addr
, &addrlen
)) {
297 fprintf(stderr
, "getsockname: %s\n", strerror(errno
));
300 printf("Server is at http://%s:%d/\n",
301 inet_ntoa(addr
.sin_addr
), ntohs(addr
.sin_port
));
304 * Now construct an fd structure to hold it.
306 f
= new_fdstruct(fd
, FD_LISTENER
);
309 * Read from standard input, and treat EOF as a notification
312 new_fdstruct(0, FD_CLIENT
);
315 * Now we're ready to run our main loop. Keep looping round on
320 int i
, j
, maxfd
, ret
;
322 #define FD_SET_MAX(fd, set, max) \
323 do { FD_SET((fd),(set)); (max) = ((max)<=(fd)?(fd)+1:(max)); } while(0)
326 * Loop round the fd list putting fds into our select
327 * sets. Also in this loop we remove any that were marked
328 * as deleted in the previous loop.
333 for (i
= j
= 0; j
< nfds
; j
++) {
335 if (fds
[j
].deleted
) {
337 free_connection(fds
[j
].cctx
);
342 switch (fds
[i
].type
) {
345 FD_SET_MAX(fds
[i
].fd
, &rfds
, maxfd
);
349 * Always read from a connection socket. Even
350 * after we've started writing, the peer might
351 * still be sending (e.g. because we shamefully
352 * jumped the gun before waiting for the end of
353 * the HTTP request) and so we should be prepared
354 * to read data and throw it away.
356 FD_SET_MAX(fds
[i
].fd
, &rfds
, maxfd
);
358 * Also attempt to write, if we have data to write.
360 if (fds
[i
].wdatapos
< fds
[i
].wdatalen
)
361 FD_SET_MAX(fds
[i
].fd
, &wfds
, maxfd
);
369 ret
= select(maxfd
, &rfds
, &wfds
, NULL
, NULL
);
371 if (ret
< 0 && (errno
!= EINTR
)) {
372 fprintf(stderr
, "select: %s", strerror(errno
));
378 for (i
= 0; i
< nfds
; i
++) {
379 switch (fds
[i
].type
) {
381 if (FD_ISSET(fds
[i
].fd
, &rfds
)) {
383 int ret
= read(fds
[i
].fd
, buf
, sizeof(buf
));
386 fprintf(stderr
, "standard input: read: %s\n",
395 if (FD_ISSET(fds
[i
].fd
, &rfds
)) {
397 * New connection has come in. Accept it.
400 struct sockaddr_in addr
;
401 socklen_t addrlen
= sizeof(addr
);
402 int newfd
= accept(fds
[i
].fd
, (struct sockaddr
*)&addr
,
405 break; /* not sure what happened there */
407 f
= new_fdstruct(newfd
, FD_CONNECTION
);
408 f
->cctx
= new_connection(t
);
409 check_magic_access(f
);
413 if (FD_ISSET(fds
[i
].fd
, &rfds
)) {
415 * There's data to be read.
420 ret
= read(fds
[i
].fd
, readbuf
, sizeof(readbuf
));
423 * This shouldn't happen in a sensible
424 * HTTP connection, so we abandon the
425 * connection if it does.
433 * If we haven't got an HTTP response
434 * yet, keep processing data in the
435 * hope of acquiring one.
437 fds
[i
].wdata
= got_data(fds
[i
].cctx
, readbuf
,
438 ret
, fds
[i
].magic_access
);
440 fds
[i
].wdatalen
= strlen(fds
[i
].wdata
);
445 * Otherwise, just drop our read data
451 if (FD_ISSET(fds
[i
].fd
, &wfds
) &&
452 fds
[i
].wdatapos
< fds
[i
].wdatalen
) {
454 * The socket is writable, and we have data to
457 int ret
= write(fds
[i
].fd
, fds
[i
].wdata
+ fds
[i
].wdatapos
,
458 fds
[i
].wdatalen
- fds
[i
].wdatapos
);
461 * Shouldn't happen; abandon the connection.
467 fds
[i
].wdatapos
+= ret
;
468 if (fds
[i
].wdatapos
== fds
[i
].wdatalen
) {
469 shutdown(fds
[i
].fd
, SHUT_WR
);