#include "uhttpd.h" void log(int type, char *s1, char *s2, int num) { int fd; char logbuffer[BUFSIZE * 2]; switch (type) { case ERROR: (void) sprintf(logbuffer, "ERROR: %s:%s Errno=%d exiting pid=%d", s1, s2, errno, getpid()); break; case SORRY: (void) sprintf(logbuffer, "

uhttpd Web Server Sorry: %s %s

\r\n", s1, s2); (void) write(num, logbuffer, strlen(logbuffer)); (void) sprintf(logbuffer, "SORRY: %s:%s", s1, s2); break; case LOG: (void) sprintf(logbuffer, " INFO: %s:%s:%d", s1, s2, num); break; } /* no checks here, nothing can be done a failure anyway */ if ((fd = open("uhttpd.log", O_CREAT | O_WRONLY | O_APPEND, 0644)) >= 0) { (void) write(fd, logbuffer, strlen(logbuffer)); (void) write(fd, "\n", 1); (void) close(fd); } if (type == ERROR || type == SORRY) exit(3); } /* this is a child web server process, so we can exit on errors */ void web(int fd, int hit) { int j, file_fd, buflen, len; long i, ret; char *fstr; static char buffer[BUFSIZE + 1]; /* static so zero filled */ ret = read(fd, buffer, BUFSIZE); /* read Web request in one go */ if (ret == 0 || ret == -1) { /* read failure stop now */ log(SORRY, "failed to read browser request", "", fd); } if (ret > 0 && ret < BUFSIZE) /* return code is valid chars */ buffer[ret] = 0; /* terminate the buffer */ else buffer[0] = 0; for (i = 0; i < ret; i++) /* remove CF and LF characters */ if (buffer[i] == '\r' || buffer[i] == '\n') buffer[i] = '*'; log(LOG, "request", buffer, hit); if (strncmp(buffer, "GET ", 4) && strncmp(buffer, "get ", 4)) log(SORRY, "Only simple GET operation supported", buffer, fd); for (i = 4; i < BUFSIZE; i++) { /* null terminate after the second space to ignore extra stuff */ if (buffer[i] == ' ') { /* string is "GET URL " +lots of other stuff */ buffer[i] = 0; break; } } for (j = 0; j < i - 1; j++) /* check for illegal parent directory use .. */ if (buffer[j] == '.' && buffer[j + 1] == '.') log(SORRY, "Parent directory (..) path names not supported", buffer, fd); if (!strncmp(&buffer[0], "GET /\0", 6) || !strncmp(&buffer[0], "get /\0", 6)) /* convert no filename to index file */ (void) strcpy(buffer, "GET /index.html"); /* work out the file type and check we support it */ buflen = strlen(buffer); fstr = (char *) 0; for (i = 0; extensions[i].ext != 0; i++) { len = strlen(extensions[i].ext); if (!strncmp(&buffer[buflen - len], extensions[i].ext, len)) { fstr = extensions[i].filetype; break; } } if (fstr == 0) log(SORRY, "file extension type not supported", buffer, fd); if ((file_fd = open(&buffer[5], O_RDONLY)) == -1) /* open the file for reading */ log(SORRY, "failed to open file", &buffer[5], fd); log(LOG, "SEND", &buffer[5], hit); (void) sprintf(buffer, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr); (void) write(fd, buffer, strlen(buffer)); /* send file in 8KB block - last block may be smaller */ while ((ret = read(file_fd, buffer, BUFSIZE)) > 0) { (void) write(fd, buffer, ret); } #ifdef LINUX sleep(1); /* to allow socket to drain */ #endif exit(1); } main(int argc, char **argv) { int i, port, pid, listenfd, socketfd, hit; size_t length; char *str; static struct sockaddr_in cli_addr; /* static = initialised to zeros */ static struct sockaddr_in serv_addr; /* static = initialised to zeros */ if (argc < 3 || argc > 3 || !strcmp(argv[1], "-?")) { (void) printf("hint: uhttpd Port-Number Top-Directory\n\n" "\tuhttpd is a small and very safe mini web server\n" "\tuhttpd only servers out file/web pages with extensions named below\n" "\t and only from the named directory or its sub-directories.\n" "\tThere is no fancy features = safe and secure.\n\n" "\tExample: uhttpd 8181 /home/nwebdir &\n\n" "\tOnly Supports:"); for (i = 0; extensions[i].ext != 0; i++) (void) printf(" %s", extensions[i].ext); (void) printf ("\n\tNot Supported: URLs including \"..\", Java, Javascript, CGI\n" "\tNot Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n" "\tNo warranty given or implied\n\tNigel Griffiths nag@uk.ibm.com\n"); exit(0); } if (!strncmp(argv[2], "/", 2) || !strncmp(argv[2], "/etc", 5) || !strncmp(argv[2], "/bin", 5) || !strncmp(argv[2], "/lib", 5) || !strncmp(argv[2], "/tmp", 5) || !strncmp(argv[2], "/usr", 5) || !strncmp(argv[2], "/dev", 5) || !strncmp(argv[2], "/sbin", 6)) { (void) printf("ERROR: Bad top directory %s, see uhttpd -?\n", argv[2]); exit(3); } if (chdir(argv[2]) == -1) { (void) printf("ERROR: Can't Change to directory %s\n", argv[2]); exit(4); } /* Become deamon + unstopable and no zombies children (= no wait()) */ if (fork() != 0) return 0; /* parent returns OK to shell */ /* XXX-jrf SIGCLD ??? */ /* (void)signal(SIGCLD, SIG_IGN); /* ignore child death */ (void) signal(SIGHUP, SIG_IGN); /* ignore terminal hangups */ for (i = 0; i < 32; i++) (void) close(i); /* close open files */ (void) setpgrp(); /* break away from process group */ log(LOG, "uhttpd starting", argv[1], getpid()); /* setup the network socket */ if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) log(ERROR, "system call", "socket", 0); port = atoi(argv[1]); if (port < 0 || port > 60000) log(ERROR, "Invalid port number (try 1->60000)", argv[1], 0); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(port); if (bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) log(ERROR, "system call", "bind", 0); if (listen(listenfd, 64) < 0) log(ERROR, "system call", "listen", 0); for (hit = 1;; hit++) { length = sizeof(cli_addr); if ((socketfd = accept(listenfd, (struct sockaddr *) &cli_addr, &length)) < 0) log(ERROR, "system call", "accept", 0); if ((pid = fork()) < 0) { log(ERROR, "system call", "fork", 0); } else { if (pid == 0) { /* child */ (void) close(listenfd); web(socketfd, hit); /* never returns */ } else { /* parent */ (void) close(socketfd); } } } }