added work for session 3
This commit is contained in:
parent
1f1cda487c
commit
e2d6d4cf9d
3 changed files with 159 additions and 128 deletions
2
session2/.gitignore
vendored
2
session2/.gitignore
vendored
|
@ -1,2 +1,4 @@
|
|||
httpd
|
||||
httpd6
|
||||
httpdgai
|
||||
acme.com/
|
||||
|
|
|
@ -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)
|
||||
|
|
270
session2/httpd.c
270
session2/httpd.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue