/* /////////////////////////////////////////////////////////////// */ /* Program : nweb.c */ /* --------------------------------------------------------------- */ /* This is a mini web server program originally written by */ /* Nigel Griffiths for Unix-based OS. */ /* */ /* The program is ported to Windows-based OS using Winsock API */ /* for a homework project for the class "Introduction to Computer */ /* Science" by Chun-Jen Tsai on 05/14/2008. */ /* */ /* Note: */ /* This mini web server only serves out web pages and files */ /* located under the directory "c:/public_html". */ /* //////////////////////////////////////////////////////////////// */ #pragma comment(lib, "WS2_32.lib") /* program use socket library */ #define _CRT_SECURE_NO_DEPRECATE #include #include #include #define BUFSIZE 8096 #define LOG_BUFSIZE (BUFSIZE<<1) #define NW_ERROR 1 #define NW_SORRY 2 #define NW_LOG 3 struct { char *ext; char *filetype; } extensions[] = { {"gif", "image/gif"}, {"jpg", "image/jpeg"}, {"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} }; void syslog(int type, char *s1, char *s2, int num) { FILE *fp; static char log_buf[LOG_BUFSIZE]; switch (type) { case NW_ERROR: sprintf(log_buf, "ERROR: %s:%s Errno = %d", s1, s2, errno); break; case NW_SORRY: sprintf(log_buf, "

nweb Web Server: %s %s

\r\n", s1, s2); send(num, log_buf, strlen(log_buf), 0); sprintf(log_buf, "SORRY: %s:%s", s1, s2); break; case NW_LOG: sprintf(log_buf, " INFO: %s:%s:%d", s1, s2, num); break; } /* write event data to the log file 'nweb.syslog' */ if ((fp = fopen("nweb.syslog", "a")) != NULL) { strncat(log_buf, "\n", LOG_BUFSIZE); fprintf(fp, log_buf); fclose(fp); } if (type == NW_ERROR || type == NW_SORRY) { WSACleanup(); exit(1); } } /* web() handles a single web request, so it's ok to exit on errors */ void web(SOCKET cli_socket, int hit) { FILE *fp; int j, buflen, len; long idx, nbytes; char *fstr; static char buffer[BUFSIZE + 1]; /* read the web browser request through the TCP connection */ nbytes = recv(cli_socket, buffer, BUFSIZE, 0); if (nbytes == 0 || nbytes == -1) { /* read failure, stop now */ syslog(NW_SORRY, "failed to read browser request", "", cli_socket); } /* check and see if we do get a request from the browser, */ if (nbytes > 0 && nbytes < BUFSIZE) { buffer[nbytes] = 0; /* conclude the buffer */ } else { buffer[0] = 0; /* empty the buffer */ } /* remove CF and LF characters */ for (idx = 0; idx < nbytes; idx++) { if (buffer[idx] == '\r' || buffer[idx] == '\n') { buffer[idx] = '*'; } } syslog(NW_LOG, "request", buffer, hit); if (strncmp(buffer, "GET ", 4) && strncmp(buffer, "get ", 4)) { syslog(NW_SORRY, "Only simple GET method is supported", buffer, cli_socket); } /* null-terminate after the second space to ignore extra stuff */ for (idx = 4; idx < BUFSIZE; idx++) { if (buffer[idx] == ' ') { /* string is "GET URL " +lots of other stuff */ buffer[idx] = 0; break; } } /* check for illegal parent directory use .. */ for (j = 0; j < idx - 1; j++) { if (buffer[j] == '.' && buffer[j+1] == '.') { syslog(NW_SORRY, "Parent directory (..) path names not supported", buffer, cli_socket); } } /* convert no filename to index file */ if (!strncmp(&buffer[0], "GET /\0", 6) || !strncmp(&buffer[0], "get /\0", 6)) { strncpy(buffer, "GET /index.html", BUFSIZE); } /* work out the file type and check if we support it */ buflen = strlen(buffer); fstr = (char *) 0; for (idx = 0; extensions[idx].ext != 0; idx++) { len = strlen(extensions[idx].ext); if (!strncmp(&buffer[buflen - len], extensions[idx].ext, len)) { fstr = extensions[idx].filetype; break; } } if (fstr == 0) { syslog(NW_SORRY, "file type not supported", buffer, cli_socket); } /* execute the send command, but first check if the browser */ /* has requested the default icon file, favicon.ico. */ if (!strncmp(buffer+5, "/favicon.ico", 12)) { /* Send a null file to satisfy a request for favicon.ico */ sprintf(buffer, "HTTP/1.0 204 NO CONTENT\r\n\r\n"); send(cli_socket, buffer, strlen(buffer), 0); } else { /* open the file for reading */ if ((fp = fopen(&buffer[5], "rb")) == NULL) { syslog(NW_SORRY, "failed to open file", &buffer[5], cli_socket); } syslog(NW_LOG, "SEND", &buffer[5], hit); sprintf(buffer, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr); send(cli_socket, buffer, strlen(buffer), 0); /* send file in 8KB block - last block may be smaller */ while ((nbytes = fread(buffer, 1, BUFSIZE, fp)) > 0) { send(cli_socket, buffer, nbytes, 0); } } } int main(int argc, char **argv) { WSADATA wsaData; SOCKET listenfd, socketfd; static struct sockaddr_in cli_addr; /* static = initialised to zeros */ static struct sockaddr_in serv_addr; /* static = initialised to zeros */ unsigned short port; int idx, hit; size_t length; if (argc > 1 && !strcmp(argv[1], "-?")) { printf("nweb is a mini web server.\n" "nweb only serves out files and web pages with\n" "the following extensions:\n"); for (idx = 0; extensions[idx].ext != 0; idx++) { printf(" %s", extensions[idx].ext); } printf("\nThe web pages/files must locate in the directory\n"); printf("'c:\\public_html' or its sub-directories.\n"); exit(0); } if (_chdrive(3) || _chdir("\\public_html")) { printf("ERROR: Can't Change to directory c:\\public_html\n"); exit(1); } /* Initialize the Winsock system library */ if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { syslog(NW_ERROR, "system call", "WSAStartup", 0); return 0; } printf("============================\n"); printf(" nweb Web server is running\n"); printf("============================\n"); syslog(NW_LOG, "nweb starting", argv[1], 0); /* setup the network socket */ if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog(NW_ERROR, "system call", "socket", 0); } /* listen to the standard HTTP port 80 */ port = 80; 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) { printf("ERROR: Can't bind a socket to port 80, another "); printf("server is running?\n"); syslog(NW_ERROR, "system call", "bind", 0); } if (listen(listenfd, 64) < 0) { syslog(NW_ERROR, "system call", "listen", 0); } /* An infinitive loop to process requests from each client. */ for (hit = 1; ; hit++) { length = sizeof(cli_addr); socketfd = accept(listenfd, (struct sockaddr *) &cli_addr, &length); if (socketfd < 0) { syslog(NW_ERROR, "system call", "accept", 0); } /* call web() to process one HTTP request */ web(socketfd, hit); shutdown(socketfd, SD_BOTH); } }