From 946abd325c983285aa5e645f4313f0e9e9318a38 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Fri, 17 Apr 2015 16:27:50 +0200 Subject: [PATCH] added session2 excercise --- session2/Makefile | 15 +++++ session2/httpd.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 session2/Makefile create mode 100644 session2/httpd.c diff --git a/session2/Makefile b/session2/Makefile new file mode 100644 index 0000000..4856020 --- /dev/null +++ b/session2/Makefile @@ -0,0 +1,15 @@ +TARGETS = httpd + +CC = gcc +CFLAGS = -g + +all: $(TARGETS) Makefile + +test: httpd + sudo ./httpd acme.com/ 80 + +clean: + rm -f $(TARGETS) + rm -f *.o *~ + +.PHONY: all clean diff --git a/session2/httpd.c b/session2/httpd.c new file mode 100644 index 0000000..b23cdda --- /dev/null +++ b/session2/httpd.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define HTTP_200 (struct status) { 200, "OK" } +#define HTTP_400 (struct status) { 400, "Bad request" } +#define HTTP_401 (struct status) { 401, "Unauthorized" } +#define HTTP_404 (struct status) { 404, "Not Found" } +#define HTTP_405 (struct status) { 405, "Method Not Allowed" } +#define HTTP_414 (struct status) { 414, "Request-URL Too Long" } +#define HTTP_413 (struct status) { 413, "Request Entity Too Large" } +#define HTTP_500 (struct status) { 500, "Internal Server Error" } + +struct status { + int code; + const char *msg; +}; + +int fd; + +const char *error_page = "" + "%1$u %2$s

%2$s

"; + +void handle_error(char *msg) +{ + perror(msg); exit(EXIT_FAILURE); +} + +struct status errno_to_status() +{ + switch (errno) { + case 0: return HTTP_200; + case EACCES: return HTTP_401; + case ENOENT: return HTTP_404; + case ENAMETOOLONG: return HTTP_414; + case EOVERFLOW: return HTTP_413; + default: return HTTP_500; + } +} + +void handle_connection(int outfd) +{ + char *method, *path, *version, *msg; + int infd; + struct stat st; + struct status status; + + FILE *f = fdopen(outfd, "r+"); + if (!f) + handle_error("Failed to open file descriptor"); + + if (fscanf(f, "%ms %ms HTTP/%ms", &method, &path, &version) == 3) { + if (!strcmp(path, "/")) { + if (!realloc(path, 32)) + handle_error("Failed realloc"); + strncpy(path, "/index.html", 32); + } + + printf(">>> %s %s HTTP/%s\n", method, path, version); + + if (strcasecmp(method, "GET")) + status = HTTP_405; + else if (stat(path, &st)) + status = errno_to_status(); + else if ((infd = open(path, O_RDONLY))) + status = errno_to_status(); + } + else + status = HTTP_400; + + printf("<<< HTTP/1.1 %u %s\r\n", status.code, status.msg); + + fprintf(f, "HTTP/1.1 %u %s\r\n", status.code, status.msg); + fprintf(f, "Connection: close\r\n"); + + switch (status.code) { + case 200: + fprintf(f, "Content-length: %u\r\n\r\n", st.st_size); + sendfile(outfd, infd, 0, st.st_size); + break; + + default: + fprintf(f, error_page, status.code, status.msg); + break; + } + + + shutdown(outfd, SHUT_RDWR); +} + +void quit() +{ + close(fd); +} + +int main(int argc, char *argv[]) +{ + char htdocs[PATH_MAX] = "/var/www"; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr.s_addr = INADDR_ANY, + .sin_port = htons(80) + }; + + if (argc > 3) { + fprintf(stderr, "usage: %s HTDOCS_DIR [PORT]\n", argv[0]); + exit(EXIT_FAILURE); + } + if (argc > 2) + sin.sin_port = htons(atoi(argv[2])); + if (argc > 1) + strncpy(htdocs, argv[1], sizeof(htdocs)); + + if (chroot(htdocs)) + handle_error("Failed to change root"); + + atexit(quit); /* Close sockets */ + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) + handle_error("Failed to create socket"); + if (bind(fd, (struct sockaddr *) &sin, sizeof(sin))) + handle_error("Failed to bind socket"); + if (listen(fd, 10)) + handle_error("Failed to listen on socket"); + + /* Main loop */ + for (;;) { + int fd2 = accept(fd, NULL, 0); + if (fork() == 0) { + close(fd); + handle_connection(fd = fd2); + exit(0); + } + else + close(fd2); + } + + if (close(fd)) + handle_error("Failed to close socket"); + + return 0; +} \ No newline at end of file