#include #include #include #include #include #include #include #include #include #include #include #include #include #define PORTMIN 2000 #define PORTMAX 2020 #define ROOT "/www/turing/htdocs/sai/ss02/soft/" #define BUFLEN 1024 volatile int running = 1; void handler (int sig) { running = 0; } /* Skip everything until we find a newline character or end of file. */ void skipline (FILE * f) { int c; while (1) { c = fgetc (f); if ((c < 0) || (c == '\n')) break; } } /* Skip everything until we find an empty line or end of file. */ void skipheader (FILE * f) { int c; while (1) { c = fgetc (f); if ((c < 0) || (c == '\n')) break; if (c == '\r') { c = fgetc (f); if ((c < 0) || (c == '\n')) break; } skipline (f); } } void error (FILE * f, int code, int doexit) { /* Always call fflush between reads and writes on the same * file descriptor. See fdopen(3C)/fopen(3C). */ printf ("DBG: Error %d\n", code); fflush (f); fprintf (f, "HTTP/1.1 %d HTTP Error\r\n", code); fprintf (f, "Content-Type: text/html\r\n\r\n"); fprintf (f, "%d HTTP Error\r\n", code); fprintf (f, "\r\n

HTTP Error (%d)

\r\n", code); fprintf (f, "Your request caused a HTTP Error Code %d\r\n", code); fprintf (f, "\r\n"); fflush (f); fclose (f); if (doexit) exit (2); } char buf[BUFLEN]; char buf2[BUFLEN]; char buf3[BUFLEN+BUFLEN]; #define MAXCHILD 10 int nrchild = 0; int main () { int sockfd, connfd, pid, fd, i; struct stat statbuf; FILE * file; struct sockaddr_in addr; struct sigaction act; /* Signalhandler installieren */ sigemptyset (&act.sa_mask); act.sa_flags = 0; act.sa_handler = handler; if (sigaction (SIGINT, &act, NULL) < 0) { perror ("sigaction"); exit (1); } if (sigaction (SIGTERM, &act, NULL) < 0) { perror ("sigaction"); exit (1); } /* create socket. HTTP uses TCP, hence SOCK_STREAM. The * third parameter can always be set to 0, at least in the * PF_INET protocol family. */ sockfd = socket (PF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror ("socket"); exit (1); } /* Try to bind the socket to a local port. This specifies the * local address. See inet(7P) on Solaris and ip(7) on linux for * more details. */ memset (&addr, 0, sizeof (struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); /* If the server is restarted several times it is likely that * the desired port is still in use. We try all port in the * range PORTMIN..PORTMAX until we find one that is not in use. */ for (i=PORTMIN; i<=PORTMAX; ++i) { int ret; addr.sin_port = htons (i); ret = bind (sockfd, (struct sockaddr *)&addr, sizeof (struct sockaddr_in)); /* Bind successful ? */ if (ret >= 0) break; /* We can tollerate failures because the port is already * in use, other errors should be fatal. */ if (errno != EADDRINUSE) { close (sockfd); perror ("bind"); exit (1); } } printf ("Listening on port %d\n", i); /* Auf ankommende Verbindungen warten. */ if (listen (sockfd, 5) < 0) { close (sockfd); perror ("listen"); exit (1); } /* Handle incoming connections until we are signaled. */ while (running) { /* Accept incoming connection. We don't care about the * address of the other end of the connection. */ connfd = accept (sockfd, NULL, NULL); if (connfd < 0) { perror ("accept"); break; } /* Check for already terminated children but don't * sleep. pid = -1 means any child. */ while (waitpid (-1, NULL, WNOHANG) > 0) nrchild--; /* Don't spawn more than MAXCHILD children. */ if (nrchild >= MAXCHILD) { /* Service unavailiable */ file = fdopen (connfd, "a+"); error (file, 503, 0); continue; } /* Create a CHILD that will handler the connection. Note that * we only wait for them after the server terminates. This * should be avoided in real life applications. */ pid = fork (); if (pid < 0) { perror ("fork"); file = fdopen (connfd, "a+"); error (file, 503, 0); continue; } if (pid > 0) { /* Parent */ /* Account for the new child. */ nrchild++; /* Don't waste our filedescriptors, the child will * handle the connection. */ close (connfd); continue; } /* Child */ close (sockfd); file = fdopen (connfd, "a+"); fgets (buf, BUFLEN, file); printf ("DBG: %s", buf); if (feof (file)) { printf ("Child %d: Connection broken\n", (int)getpid()); close (connfd); exit (1); } if (strchr (buf, '\n') == NULL) { /* Request too long */ error (file, 414, 1); } skipheader (file); if ((strncasecmp (buf, "GET", 3) != 0) || !isspace ((int)(buf[3]))) error (file, 501, 1); if (sscanf (buf+3, "%s", buf2) != 1) error (file, 400, 1); if (buf2[strlen(buf2)-1] == '/') { sprintf (buf3, "%s/%sindex.html", ROOT, buf2); } else { sprintf (buf3, "%s/%s", ROOT, buf2); } if (strstr (buf3, "..")) error (file, 403, 1); fd = open (buf3, O_RDONLY); if (fd < 0) { /* File doesn't exist */ if ((errno == EPERM) || (errno == EACCES)) { error (file, 403, 1); } else { error (file, 404, 1); } } /* Check if file is a regular file. */ if (fstat (fd, &statbuf) < 0) { perror ("fstat"); error (file, 500, 1); } if ((statbuf.st_mode & S_IFMT) != S_IFREG) { error (file, 404, 1); } fflush (file); printf ("DBG: File %s\n", buf3); fprintf (file, "HTTP/1.1 200 OK\r\n"); fprintf (file, "Content-Type: text/html\r\n"); fprintf (file, "\r\n"); while (1) { int ret; ret = read (fd, buf, BUFLEN); if (ret == 0) break; if (fwrite (buf, ret, 1, file) < 0) perror ("fwrite"); } close (fd); fflush (file); fclose (file); sleep (1000); exit (0); } close (sockfd); printf ("Waiting for children\n"); while (1) { int ret = wait (NULL); if ((ret < 0) && (errno == ECHILD)) break; } return 0; }