//====================================================== file = weblite.c ===== //= A super light-weight secure HTTP server = //= - Uses threads to allow for parallel connections = //============================================================================= //= Notes: = //= 1) Compiles for Windows (using winsock and Windows threads) and Unix = //= (using BSD sockets and POSIX threads) = //= 2) Serves HTML, text, and GIF only. = //= 3) Serves files from directory that weblite is running in. This = //= makes weblite "secure". = //= 4) Sometimes the browser drops a connection when doing a refresh. = //= This is handled by checking the recv() return code in the = //= function that handles GETs. This is only seen when using = //= Explorer. = //= 5) The 404 HTML message does not always display in Explorer. = //= 6) Ignore the compile-time warnings regarding unreachable code = //= in main(). = //=---------------------------------------------------------------------------= //= Execution notes: = //= 1) Execute this program in the directory which will be the root for = //= all file references (i.e., the directory that is considered at = //= "public.html"). = //= 2) Open a Web browser and surf to http://xxx.xxx.xxx.xxx/yyy where = //= xxx.xxx.xxx.xxx is the IP address or hostname of the machine that = //= weblite is executing on and yyy is the requested object. = //= 3) The only non-error output (to stdout) from weblite is a message = //= with the name of the file currently being sent. = //=---------------------------------------------------------------------------= //= Build: Windows: bcc32 -WM weblite.c, cl /MT weblite.c wsock32.lib = //= Unix: gcc weblite.c -lpthread -lsocket -lnsl -o weblite = //=---------------------------------------------------------------------------= //= Execute: weblite = //=---------------------------------------------------------------------------= //= History: KJC (10/08/02) - Genesis = //= KJC (09/11/05) - Fixed "GET \./../" security hole = //= KJC (01/29/06) - Add BSD as conditional compile (thanks to = //= James Poag for POSIX threads howto) = //= KJC (12/06/06) - Fixed pthread call (thanks to Nicholas = //= Paltzer for finding and fixing the problem) = //============================================================================= #define WIN // WIN for Winsock and BSD for BSD sockets //----- Include files --------------------------------------------------------- #include // Needed for printf() #include // Needed for exit() #include // Needed for memcpy() and strcpy() #include // Needed for file i/o stuff #ifdef WIN #include // Needed for _beginthread() and _endthread() #include // Needed for _threadid #include // Needed for all Winsock stuff #include // Needed for file i/o constants #include // Needed for file i/o stuff #endif #ifdef BSD #include // Needed for pthread_create() and pthread_exit() #include // Needed for file i/o constants #include // Needed for system defined identifiers. #include // Needed for internet address structure. #include // Needed for socket(), bind(), etc... #include // Needed for inet_ntoa() #include // Needed for network stuff #endif //----- HTTP response messages ---------------------------------------------- #define OK_IMAGE "HTTP/1.0 200 OK\r\nContent-Type:image/gif\r\n\r\n" #define OK_TEXT "HTTP/1.0 200 OK\r\nContent-Type:text/html\r\n\r\n" #define NOTOK_404 "HTTP/1.0 404 Not Found\r\nContent-Type:text/html\r\n\r\n" #define MESS_404 "

FILE NOT FOUND

" //----- Defines ------------------------------------------------------------- #define PORT_NUM 80 // Port number for a Web server #define BUF_SIZE 1024 // Buffer size (big enough for a GET) //----- Function prototypes ------------------------------------------------- #ifdef WIN void handle_get(void *in_arg); // Windows thread function to handle GET #endif #ifdef BSD void *handle_get(void *in_arg); // POSIX thread function to handle GET #endif //===== Main program ======================================================== int main(void) { #ifdef WIN WORD wVersionRequested = MAKEWORD(1,1); // Stuff for WSA functions WSADATA wsaData; // Stuff for WSA functions #endif int server_s; // Server socket descriptor struct sockaddr_in server_addr; // Server Internet address int client_s; // Client socket descriptor struct sockaddr_in client_addr; // Client Internet address struct in_addr client_ip_addr; // Client IP address #ifdef WIN int addr_len; // Internet address length #endif #ifdef BSD socklen_t addr_len; // Internet address length pthread_t thread_id; // Thread ID #endif #ifdef WIN // This stuff initializes winsock WSAStartup(wVersionRequested, &wsaData); #endif // Create a socket, fill-in address information, and then bind it server_s = socket(AF_INET, SOCK_STREAM, 0); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT_NUM); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(server_s, (struct sockaddr *)&server_addr, sizeof(server_addr)); // Set-up the listen listen(server_s, 100); // Main loop to accept connections and then spin-off thread to handle the GET printf(">>> weblite is running on port %d <<< \n", PORT_NUM); while(1) { addr_len = sizeof(client_addr); client_s = accept(server_s, (struct sockaddr *)&client_addr, &addr_len); if (client_s == -1) { printf("ERROR - Unable to create a socket \n"); exit(1); } #ifdef WIN if (_beginthread(handle_get, 4096, (void *)client_s) < 0) #endif #ifdef BSD if (pthread_create(&thread_id, NULL, handle_get, (void *)client_s) != 0) #endif { printf("ERROR - Unable to create a thread to handle the GET \n"); exit(1); } } return(0); } //=========================================================================== //= This is is the thread function to handle the GET = //=========================================================================== #ifdef WIN void handle_get(void *in_arg) #endif #ifdef BSD void *handle_get(void *in_arg) #endif { int client_s; // Client socket descriptor char in_buf[BUF_SIZE]; // Input buffer for GET request char out_buf[BUF_SIZE]; // Output buffer for HTML response int fh; // File handle int buf_len; // Buffer length for file reads char command[BUF_SIZE]; // Command buffer char file_name[BUF_SIZE]; // File name buffer int retcode; // Return code // Set client_s to in_arg client_s = (int) in_arg; // Receive the (presumed) GET request from the Web browser retcode = recv(client_s, in_buf, BUF_SIZE, 0); // If the recv() return code is bad then bail-out (see note #3) if (retcode <= 0) { printf("ERROR - Receive failed --- probably due to dropped connection \n"); #ifdef WIN closesocket(client_s); _endthread(); #endif #ifdef BSD close(client_s); pthread_exit(NULL); #endif } // Parse out the command from the (presumed) GET request and filename sscanf(in_buf, "%s %s \n", command, file_name); // Check if command really is a GET, if not then bail-out if (strcmp(command, "GET") != 0) { printf("ERROR - Not a GET --- received command = '%s' \n", command); #ifdef WIN closesocket(client_s); _endthread(); #endif #ifdef BSD close(client_s); pthread_exit(NULL); #endif } // It must be a GET... open the requested file // - Start at 2nd char to get rid of leading "\" #ifdef WIN fh = open(&file_name[1], O_RDONLY | O_BINARY, S_IREAD | S_IWRITE); #endif #ifdef BSD fh = open(&file_name[1], O_RDONLY, S_IREAD | S_IWRITE); #endif // If file does not exist, then return a 404 and bail-out if (fh == -1) { printf("File '%s' not found --- sending an HTTP 404 \n", &file_name[1]); strcpy(out_buf, NOTOK_404); send(client_s, out_buf, strlen(out_buf), 0); strcpy(out_buf, MESS_404); send(client_s, out_buf, strlen(out_buf), 0); #ifdef WIN closesocket(client_s); _endthread(); #endif #ifdef BSD close(client_s); pthread_exit(NULL); #endif } // Check that filename does not start with a ".", "/", "\", or have a ":" in // the second position indicating a disk identifier (e.g., "c:"). // - This is a security check to prevent grabbing any file on the server if ((file_name[1] == '.') || (file_name[1] == '/') || (file_name[1] == '\\') || (file_name[2] == ':')) { printf("SECURITY VIOLATION --- trying to read '%s' \n", &file_name[1]); close(fh); #ifdef WIN closesocket(client_s); _endthread(); #endif #ifdef BSD close(client_s); pthread_exit(NULL); #endif } // Generate and send the response printf("Sending file '%s' \n", &file_name[1]); if (strstr(file_name, ".gif") != NULL) strcpy(out_buf, OK_IMAGE); else strcpy(out_buf, OK_TEXT); send(client_s, out_buf, strlen(out_buf), 0); while(1) { buf_len = read(fh, out_buf, BUF_SIZE); if (buf_len == 0) break; send(client_s, out_buf, buf_len, 0); } // Close the file, close the client socket, and end the thread close(fh); #ifdef WIN closesocket(client_s); _endthread(); #endif #ifdef BSD close(client_s); pthread_exit(NULL); #endif }