From e2d6d4cf9d8e91e91ee338096f128b16c8e5d291 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sat, 25 Apr 2015 16:27:05 +0200 Subject: [PATCH] added work for session 3 --- session2/.gitignore | 2 + session2/Makefile | 15 ++- session2/httpd.c | 270 ++++++++++++++++++++++++-------------------- 3 files changed, 159 insertions(+), 128 deletions(-) diff --git a/session2/.gitignore b/session2/.gitignore index 41965d6..67d5972 100644 --- a/session2/.gitignore +++ b/session2/.gitignore @@ -1,2 +1,4 @@ httpd +httpd6 +httpdgai acme.com/ diff --git a/session2/Makefile b/session2/Makefile index f298dae..a7e3a80 100644 --- a/session2/Makefile +++ b/session2/Makefile @@ -1,13 +1,20 @@ -TARGETS = httpd +TARGETS = httpd httpd6 httpdgai CC = gcc CFLAGS = -g all: $(TARGETS) Makefile - + +httpd6: CFLAGS=-DIPV6 +httpdgai: CFLAGS=-DGAI + +httpdgai httpd httpd6: httpd.c readline.c + $(CC) $(CFLAGS) $^ -o $@ + test: httpd - wget -np -e robots=off --recursive --level=1 --domains=acme.com http://acme.com - sudo ./httpd acme.com/ 80 + sudo -i && ./httpd www 80 & + wget http://localhost:80 + killall httpd clean: rm -f $(TARGETS) diff --git a/session2/httpd.c b/session2/httpd.c index b23cdda..e6a947c 100644 --- a/session2/httpd.c +++ b/session2/httpd.c @@ -1,156 +1,178 @@ #include #include -#include +#include #include -#include +#include #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" } +#include "readline.h" -struct status { - int code; - const char *msg; -}; +#define HTTP(c, m) (struct status) { .code = c, .msg = m } -int fd; +static const char err[] = "\r\n" + "\r\n

%u %s

\r\n"; -const char *error_page = "" - "%1$u %2$s

%2$s

"; +static int fd; -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() -{ +void quit() { close(fd); } -int main(int argc, char *argv[]) -{ - char htdocs[PATH_MAX] = "/var/www"; - struct sockaddr_in sin = { +void request(int outfd) { + struct stat st; + struct status{ int code; const char *msg; } rc; + + int infd; + char *method, *path, *version, line[1024]; + if (readline(outfd, line, sizeof(line)) > 0) { + method = strtok(line, " "); + path = strtok(NULL, " "); + version = strtok(NULL, "\r\n"); + + if (!method || !path || !version) + rc = HTTP(400, "Bad Request"); + + if (strcasecmp(method, "GET")) + rc = HTTP(405, "Method Not Allowed"); + else if (strcasecmp(version, "HTTP/1.1")) + rc = HTTP(505, "HTTP Version not supported"); + else { + if (!strcmp(path, "/")) + path = "/index.html"; + + errno = 0; + if (stat(path, &st) || !S_ISREG(st.st_mode)) + goto err; + + infd = open(path, O_RDONLY); +err: switch (errno) { + case 0: rc = HTTP(200, "OK"); break; + case EACCES: rc = HTTP(403, "Forbidden"); break; + case ENOENT: rc = HTTP(404, "Not Found"); break; + case ENAMETOOLONG: rc = HTTP(414, "Request-URL Too Long"); break; + default: rc = HTTP(500, "Internal Server Error"); break; + } + } + + printf(" >>> %s %s %s\r\n", method, path, version); /* debug */ + } + else + rc = HTTP(400, "Bad Request"); + + printf(" <<< HTTP/1.1 %u %s\r\n", rc.code, rc.msg); + + dprintf(outfd, "HTTP/1.1 %u %s\r\n", rc.code, rc.msg); + dprintf(outfd, "Connection: close\r\n"); + + switch (rc.code) { + case 200: + dprintf(outfd, "Content-length: %lu\r\n\r\n", st.st_size); + + if (!sendfile(outfd, infd, 0, st.st_size)) + error(-1, errno, "sendfile"); + + close(infd); + break; + + default: + dprintf(outfd, err, rc.code, rc.msg); + break; + } +} + +int main(int argc, char *argv[]) { + int val = 1; + char lname[NI_MAXHOST], lserv[NI_MAXSERV]; + + if (argc != 3 && argc != 4) + error(-1, 0, "usage: %s HTDOCS PORT\n", argv[0]); + + char *host = argc == 4 ? argv[3] : NULL; + char *port = argv[2]; + +#ifdef GAI + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_flags = AI_PASSIVE | AI_ADDRCONFIG, + .ai_socktype = SOCK_STREAM + }, *res; + + if (getaddrinfo(host, port, &hints, &res)) + error(-1, errno, "Failed to get socket address"); + + struct sockaddr *sap = res[0].ai_addr; + socklen_t salen = res[0].ai_addrlen; +#else + #ifdef IPV6 + struct sockaddr_in6 sa = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_ANY_INIT, + .sin6_port = htons(atoi(argv[2])) + }; + #else + struct sockaddr_in sa = { .sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY, - .sin_port = htons(80) + .sin_port = htons(atoi(argv[2])) }; - - 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 */ + #endif + struct sockaddr *sap = (struct sockaddr *) &sa; + socklen_t salen = sizeof(sa); +#endif - fd = socket(AF_INET, SOCK_STREAM, 0); + if (getnameinfo(sap, salen, + lname, sizeof(lname), lserv, sizeof(lserv), 0)) + error(-1, errno, "Failed to get name"); + + fd = socket(sap->sa_family, 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"); + error(-1, errno, "Failed to create socket"); + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) + error(-1, errno, "Failed setsockopt"); + if (bind(fd, sap, salen)) + error(-1, errno, "Failed to bind socket"); if (listen(fd, 10)) - handle_error("Failed to listen on socket"); - - /* Main loop */ + error(-1, errno, "Failed to listen on socket"); + if (chroot(argv[1])) + error(-1, errno, "Failed to enter chroot"); + + atexit(quit); +#ifdef GAI + freeaddrinfo(res); +#endif + for (;;) { - int fd2 = accept(fd, NULL, 0); + struct sockaddr_storage peer; + socklen_t peerlen = sizeof(peer); + + int fd2 = accept(fd, (struct sockaddr *) &peer, &peerlen); + + char pname[NI_MAXHOST], pserv[NI_MAXSERV]; + if (getnameinfo((struct sockaddr *) &peer, peerlen, + pname, sizeof(pname), pserv, sizeof(pserv), 0)) + error(-1, errno, "Failed to get name of peer"); + + printf("%s:%s => %s:%s\n", + pname, pserv, lname, lserv); + if (fork() == 0) { close(fd); - handle_connection(fd = fd2); + request(fd = fd2); + shutdown(fd, SHUT_RDWR); exit(0); } else close(fd2); } - if (close(fd)) - handle_error("Failed to close socket"); - return 0; -} \ No newline at end of file +}