/* * This 'Tiny Web Server’ is derived from nweb, which was developed * by Nigel Griffiths and published at * https://www.ibm.com/developerworks/systems/library/es-nweb/index.html. * * The web server was released under the IBM License Agreement. */ #include #include #include #include #include #include #include #include #include #include #ifndef __hermit__ #include #endif #define VERSION 23 #define BUFSIZE 8096 #define ERROR 42 #define LOG 44 #define FORBIDDEN 403 #define NOTFOUND 404 struct { char *ext; char *filetype; } extensions [] = { {"gif", "image/gif" }, {"jpg", "image/jpg" }, {"jpeg","image/jpeg"}, {"png", "image/png" }, {"ico", "image/ico" }, {"zip", "image/zip" }, {"gz", "image/gz" }, {"tar", "image/tar" }, {"htm", "text/html" }, {"html","text/html" }, {0,0} }; static ssize_t nweb_write(int fd, const char* buf, size_t len) { size_t pos = 0; while (pos < len) { ssize_t r = write(fd, buf+pos, len-pos); if (r > 0) pos += r; } return pos; } static void logger(int type, char *s1, char *s2, int socket_fd) { 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 FORBIDDEN: (void)nweb_write(socket_fd, "HTTP/1.1 403 Forbidden\nContent-Length: 185\nConnection: close\nContent-Type: text/html\n\n\n403 Forbidden\n\n

Forbidden

\nThe requested URL, file type or operation is not allowed on this simple static file webserver.\n\n",271); (void)sprintf(logbuffer,"FORBIDDEN: %s:%s",s1, s2); break; case NOTFOUND: (void)nweb_write(socket_fd, "HTTP/1.1 404 Not Found\nContent-Length: 136\nConnection: close\nContent-Type: text/html\n\n\n404 Not Found\n\n

Not Found

\nThe requested URL was not found on this server.\n\n",224); (void)sprintf(logbuffer,"NOT FOUND: %s:%s",s1, s2); break; case LOG: (void)sprintf(logbuffer," INFO: %s:%s:%d",s1, s2,socket_fd); break; } /* No checks here, nothing can be done with a failure anyway */ if((fd = open("nweb.log", O_CREAT| O_WRONLY | O_APPEND,0644)) >= 0) { (void)nweb_write(fd,logbuffer,strlen(logbuffer)); (void)nweb_write(fd,"\n",1); (void)close(fd); } if(type == ERROR || type == NOTFOUND || type == FORBIDDEN) exit(3); } /* this is a child web server process, so we can exit on errors */ static void web(int fd, int hit) { int j, file_fd, buflen; long i, ret, len; char * fstr; 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 */ logger(FORBIDDEN,"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 0 ) { (void)nweb_write(fd,buffer,ret); } #ifdef __hermit__ close(fd); #else sleep(1); /* allow socket to drain before signalling the socket is closed */ close(fd); exit(1); #endif } #ifdef __hermit__ #include typedef struct { int fd; int hit; } web_t; static void* thread_func(void* arg) { web_t* w = (web_t*) arg; web(w->fd,w->hit); free(w); return NULL; } static void webthr(int fd, int hit) { web_t* w = malloc(sizeof(web_t)); w->fd = fd; w->hit = hit; int ret = pthread_create(NULL, NULL, thread_func, (void*) w); if (ret) fprintf(stderr, "Unable to create thread: %d\n", ret); sched_yield(); } #endif int main(int argc, char **argv) { int i, port, pid, listenfd, socketfd, hit; socklen_t length; static struct sockaddr_in cli_addr; /* static = initialised to zeros */ static struct sockaddr_in serv_addr; /* static = initialised to zeros */ #ifndef __hermit__ if( argc < 3 || argc > 3 || !strcmp(argv[1], "-?") ) { (void)printf("hint: nweb Port-Number Top-Directory\t\tversion %d\n\n" "\tnweb is a small and very safe mini web server\n" "\tnweb 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: nweb 8181 /home/nwebdir &\n\n" "\tOnly Supports:", VERSION); 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 nweb -?\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 */ (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 */ #else if( argc < 2 || argc > 2 || !strcmp(argv[1], "-?") ) { (void)printf("hint: nweb Port-Number Top-Directory\t\tversion %d\n\n" "\tnweb is a small and very safe mini web server\n" "\tnweb 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: nweb 8181 &\n\n" "\tSupported: current working directory\n" "\tOnly Supports:", VERSION); 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" "\tNo warranty given or implied\n\tNigel Griffiths nag@uk.ibm.com\n" ); exit(0); } #endif logger(LOG,"nweb starting",argv[1],getpid()); /* setup the network socket */ if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0) logger(ERROR, "system call","socket",0); port = atoi(argv[1]); if(port < 0 || port >60000) logger(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) logger(ERROR,"system call","bind",0); if( listen(listenfd,64) <0) logger(ERROR,"system call","listen",0); for(hit=1; ;hit++) { length = sizeof(cli_addr); if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0) logger(ERROR,"system call","accept",0); #ifdef __hermit__ //webthr(socketfd,hit); web(socketfd,hit); #else if((pid = fork()) < 0) { logger(ERROR,"system call","fork",0); } else { if(pid == 0) { /* child */ (void)close(listenfd); web(socketfd,hit); /* never returns */ } else { /* parent */ (void)close(socketfd); } } #endif } }