added work for session 3

This commit is contained in:
Steffen Vogel 2015-04-25 16:27:05 +02:00
parent 1f1cda487c
commit e2d6d4cf9d
3 changed files with 159 additions and 128 deletions

2
session2/.gitignore vendored
View file

@ -1,2 +1,4 @@
httpd
httpd6
httpdgai
acme.com/

View file

@ -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)

View file

@ -1,156 +1,178 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <sys/sendfile.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#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<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
"\r\n<html><body><h1>%u %s</h1></body></html>\r\n";
const char *error_page = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"
"<html><head><title>%1$u %2$s</title></head><body><h1>%2$s</h1></body></html>";
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;
}
}