ethernet: add a working Linux/C webserver as a reference

This commit is contained in:
Vince Weaver 2016-12-01 15:09:13 -05:00
parent 89f9e15ab5
commit 4f3017c723
5 changed files with 408 additions and 2 deletions

View File

@ -2,7 +2,9 @@ CC = gcc
CFLAGS = -Wall -O2
LFLAGS =-lpcap
all: testarp
all: testarp webserver
###
testarp: testarp.o
$(CC) $(LFLAGS) -o testarp testarp.o
@ -10,5 +12,15 @@ testarp: testarp.o
testarp.o: testarp.c
$(CC) $(CFLAGS) -c testarp.c
###
webserver: webserver.o
$(CC) $(LFLAGS) -o webserver webserver.o
webserver.o: webserver.c
$(CC) $(CFLAGS) -c webserver.c
###
clean:
rm -f *~ *.o testarp
rm -f *~ *.o testarp webserver

14
ethernet/c/about.html Normal file
View File

@ -0,0 +1,14 @@
<html>
<head><title>About the VMW-web server</title></head>
<body>
<h1>About the VMW-web server</h1>
I made it myself.
<hr>
<a href="/">Back</a>
</body>
</html>

23
ethernet/c/index.html Normal file
View File

@ -0,0 +1,23 @@
<html>
<head><title>Sample web Page</title></head>
<body>
<center><img src="vmw_logo.png"></center>
<h1><center>Sample Web Page</center></h1>
Just testing out a barebones webserver for ECE435.<br><br>
<a href="about.html">More info</a><br><br>
<a href="invalid.html">Invalid Link</a><br><br>
<a href="teapot.html">Teapot Link</a><br><br>
<hr>
Back to <a href="http://www.deater.net/weave/vmwprod/">My Web Page</a>
</body>
</html>

BIN
ethernet/c/vmw_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

357
ethernet/c/webserver.c Normal file
View File

@ -0,0 +1,357 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>
#define BUFFER_SIZE 256
/* assume the max command we get is this big */
#define RECEIVE_BUFFER_SIZE 4096
/* Default port to listen on */
#define DEFAULT_PORT 80
#define MIME_HTML 0
#define MIME_TXT 1
#define MIME_PNG 2
#define MIME_JPG 3
#define MIME_MAX 4
struct mime_type {
char extension[5];
char string[32];
};
struct mime_type mime[MIME_MAX] = {
{"html", "text/html"},
{"txt", "text/plain"},
{"png", "image/png"},
{"jpg", "image/jpeg"},
};
static int detect_mime(char *filename) {
int i,j;
i=strlen(filename);
while(i>0) {
if (filename[i]=='.') break;
i--;
}
i++;
for(j=0;j<MIME_MAX;j++) {
if (!strcmp(filename+i,mime[j].extension)) {
printf("Found filetype %s\n",mime[j].string);
return j;
}
}
return MIME_HTML;
}
static int serve_file(int fd, char *filename) {
int n,result,mime_type,out_size=0;
char time_string[1000];
time_t now = time(NULL);
struct tm tm = *gmtime(&now);
struct stat stat_buf;
char out_buffer[4096];
strftime(time_string, sizeof(time_string),
"%a, %d %b %Y %H:%M:%S %Z", &tm);
result=stat(filename,&stat_buf);
mime_type=detect_mime(filename);
if (strstr(filename,"teapot")) {
char message418[]=
"<html><head>\r\n"
"<title>418 I'm a teapot</title>\r\n"
"</head><body>\r\n"
"<h1>I'm a Teapot</h1>\r\n"
"<p>I'm a *little* teapot.<br />\r\n"
"</p>\r\n"
"</body></html>\r\n";
sprintf(out_buffer,
"HTTP/1.1 418 I'm a teapot\r\n"
"Date: %s\r\n"
"Server: VMW-web\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
"\r\n"
"%s",
time_string,(int)strlen(message418),message418);
out_size=strlen(out_buffer);
} else if (result<0) {
char message404[]=
"<html><head>\r\n"
"<title>400 Bad Request</title>\r\n"
"</head><body>\r\n"
"<h1>Bad Request</h1>\r\n"
"<p>Your browser sent a request that this server could not understand.<br />\r\n"
"</p>\r\n"
"</body></html>\r\n";
sprintf(out_buffer,
"HTTP/1.1 400 Bad Request\r\n"
"Date: %s\r\n"
"Server: VMW-web\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
"\r\n"
"%s",
time_string,(int)strlen(message404),message404);
out_size=strlen(out_buffer);
}
else {
char mod_string[1000];
tm = *gmtime(&stat_buf.st_mtime);
int in_fd,out_offset;
char file_buffer[256];
strftime(mod_string, sizeof(mod_string),
"%a, %d %b %Y %H:%M:%S %Z", &tm);
sprintf(out_buffer,
"HTTP/1.1 200 OK\r\n"
"Date: %s\r\n"
"Server: VMW-web\r\n"
"Last-Modified: %s\r\n"
"Content-Length: %ld\r\n"
"Content-Type: %s\r\n"
"\r\n",
time_string,
mod_string,
stat_buf.st_size,
mime[mime_type].string);
out_offset=strlen(out_buffer);
in_fd=open(filename,O_RDONLY);
if (in_fd<0) {
fprintf(stderr,"Could not open %s\n",filename);
} else {
while(1) {
result=read(in_fd,&file_buffer,256);
if (result<=0) break;
memcpy(out_buffer+out_offset,
file_buffer,result);
out_offset+=result;
}
close(in_fd);
}
out_size=out_offset;
}
/* Send a response */
printf("Sending:\n");
printf("%s\n",out_buffer);
n = write(fd,out_buffer,out_size);
if (n<0) {
fprintf(stderr,"Error writing. %s\n",
strerror(errno));
}
return 0;
}
int main(int argc, char **argv) {
int socket_fd,new_socket_fd;
struct sockaddr_in server_addr, client_addr;
struct addrinfo hints,*server_info;
int port=DEFAULT_PORT;
int n;
socklen_t client_len;
char buffer[BUFFER_SIZE];
char in[RECEIVE_BUFFER_SIZE];
int i,result,in_ptr=0;
char port_string[BUFFER_SIZE];
if (argc>1) {
port=atoi(argv[1]);
}
sprintf(port_string,"%d",port);
printf("Starting server on port %d\n",port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
/* Null means localhost */
/* How can we get IP of default interface? */
result=getaddrinfo(NULL,port_string,&hints,&server_info);
if (result<0) {
fprintf(stderr,"Error getaddrinfo!\n");
return -1;
}
/* Open a socket to listen on */
/* AF_INET means an IPv4 connection */
/* SOCK_STREAM means reliable two-way connection (TCP) */
socket_fd = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
if (socket_fd<0) {
fprintf(stderr,"Error opening socket! %s\n",
strerror(errno));
return -1;
}
/* Set up the server address to listen on */
memset(&server_addr,0,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
/* Convert the port we want to network byte order */
server_addr.sin_port=htons(port);
/* Bind to the port */
if (bind(socket_fd, server_info->ai_addr,server_info->ai_addrlen)) {
fprintf(stderr,"Error binding! %s\n", strerror(errno));
return -1;
}
/* Tell the server we want to listen on the port */
/* Second argument is backlog, how many pending connections can */
/* build up */
listen(socket_fd,5);
listen_connection:
/* Call accept to create a new file descriptor for an incoming */
/* connection. It takes the oldest one off the queue */
/* We're blocking so it waits here until a connection happens */
client_len=sizeof(client_addr);
new_socket_fd = accept(socket_fd,
(struct sockaddr *)&client_addr,&client_len);
if (new_socket_fd<0) {
fprintf(stderr,"Error accepting! %s\n",strerror(errno));
}
in_ptr=0;
while(1) {
/* Someone connected! Let's try to read BUFFER_SIZE-1 bytes */
memset(buffer,0,BUFFER_SIZE);
n = read(new_socket_fd,buffer,(BUFFER_SIZE-1));
if (n==0) {
fprintf(stderr,"Connection to client lost\n\n");
break;
}
else if (n<0) {
fprintf(stderr,"Error reading from socket %s\n",
strerror(errno));
}
if (in_ptr+n>RECEIVE_BUFFER_SIZE) {
fprintf(stderr,"Overflow receive buffer!\n");
break;
}
memcpy(&in[in_ptr],buffer,n);
in_ptr+=n;
in[in_ptr]=0;
/* See if last chars were CRLFCRLF (empty line) */
/* Which indicates done talking */
if ((in[in_ptr-1]=='\n') && (in[in_ptr-3]=='\n')) {
break;
}
}
/* Print the message we received */
char filename[BUFSIZ];
printf("Request from webserver:\n");
i=0;
int found_filename=0,file_ptr=0;
while(i<strlen(in)) {
if (!found_filename) {
if (in[i]=='G') {
printf("G"); i++;
if (in[i]=='E') {
printf("E"); i++;
if (in[i]=='T') {
printf("T"); i++;
if (in[i]==' ') {
printf(" "); i++;
found_filename=1;
while(in[i]!=' ') {
filename[file_ptr]=in[i];
printf("%c",in[i]);
i++;
file_ptr++;
if (file_ptr>BUFSIZ) break;
}
filename[file_ptr]=0;
}
}
}
}
}
printf("%c",in[i]);
i++;
}
if (!strcmp(filename,"/")) {
printf("Mapping / to index.html\n");
strcpy(filename,"/index.html");
}
printf("Sending back file: %s\n",filename);
/* Skip leading / */
serve_file(new_socket_fd,filename+1);
printf("Exiting server\n\n");
/* Try to avoid TIME_WAIT */
// sleep(1);
/* Close the sockets */
close(new_socket_fd);
goto listen_connection;
close(socket_fd);
return 0;
}