/* * shttpd.cpp * Simple HTTP Server * Copyright 2009 Marcin Biernacki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ /* * * Include what we need: network stuff, string... * */ #include /* clear */ #include #include #include // to use umask function.... #include /* syslog logging.... */ #include #include #include /* bind(), listen(), accept() */ #include /* waitpid() */ #include /* fork() */ #include /* signal() */ #include #include #include using namespace std; /* * Configuration variables * */ #define VERSION 0.1 #define AUTHOR "Marcin Biernacki" #define banner "shttpd 0.1 (Unix)" string root_dir="/home/shttpd/public_html/"; int port=7777; int max_conn=100; /* * * Other variables * */ int sock; int sock_client; int size_of_saddr; struct sockaddr_in saddr,client_addr; int error_fork; // INFO string message; string *http_variables; // variables from client string *http_values; // values from client string http_host; // result of "Host:" string proto; // protocol info string user_agent; // User-Agent: ... int no_variables; // number of variables /* * Our functions * */ bool make_daemon(); // makes that program a daemon void sigchld(int not_used); void sigterm(int not_used); void sigusrone(int not_used); void sigkill(int not_used); int main(); int talk(); // communication with client bool security(string &to_check); // check if there are any harmful characters bool http_head_get(string &adres,string &question,string &virtualhost); // GET+HEAD request support /* * make me a daemon! */ bool make_daemon() { pid_t child,sid_of_child; /* process id of Child */ child = fork(); // Create new process. if (child < 0) { syslog(LOG_ERR,"could not create daemon process. PID=%d",getpid()); return false; }; if (child > 0) { // Success! syslog(LOG_INFO,"daemon process created. PID=%d",getpid()); exit(EXIT_SUCCESS); // end of parent process.... }; /* * making process independent. */ umask(0); sid_of_child = setsid(); if (sid_of_child < 0) { syslog(LOG_ERR,"setsid() failed in phttpd daemon. PID=%d",getpid()); return false; }; if(chdir(root_dir.c_str())){ syslog(LOG_ERR,"chdir() failure. Can't change directory to root_dir"); exit(EXIT_FAILURE); }; close(STDERR_FILENO); close(STDIN_FILENO); close(STDOUT_FILENO); // close file descriptors. signal(SIGCHLD, sigchld); //to check `zombie processes` signal(SIGKILL, sigkill); signal(SIGTERM, sigterm); signal(SIGUSR1, sigusrone); return true; }; /* * The most important - HTTP talk * */ int talk() { /* * Get first part of request (GET | HEAD) */ cin >> message; security(message); if((message=="GET")||(message=="HEAD")) { /* * Get location, check corectness. */ string request=message; // copy request name (GET | HEAD) cin >> message; // read location cin >> proto; // get protocol info if(!security(message)) return false; /* * Get information from client * */ char info[100],info2[100]; cin.ignore(1,'\n'); while(true) { cin.get(info,98,' '); cin.ignore(1,' '); cin.get(info2,98); /* * get useful information from client * */ if(!strcmp(info,"Host:"))http_host=info2; if(!strcmp(info,"User-Agent:"))user_agent=info2; cin.ignore(1,'\n'); if((cin.peek()==10)||(cin.peek()==13))break; }; /* * Show the file */ http_head_get(message,request,http_host); }; return true; }; /* * Security input analyzer * */ bool security(string &to_check) { // check length (414 error?) if(to_check.length()>254) { cout << "HTTP/1.1 414 Request Too Long" << endl; cout << "Server: " << banner << endl; cout << "Connection: close" << endl; cout << "Content-Type: text/html" << endl; cout << "\n

414 Error

Your browser sent too long request.

" << endl; cout << endl; return false; }; // check if there are any "/../" constructions. // prevents changing workdir for(int i=0;i<=(signed int)to_check.length();i++) { if((i>=1)&&(to_check[i]=='.')&&(to_check[i-1]=='.')) { cout << "HTTP/1.1 400 Bad request" << endl; cout << "Server: " << banner << endl; cout << "Connection: close" << endl; cout << "Content-Type: text/html" << endl; cout << endl; cout << "

400 Error

Invalid request.

" << endl; cout << endl; return false; } }; return true; }; /* * HTTP GET REQUEST */ bool http_head_get(string &adres,string &question,string &virtualhost) { /* * Get information from request */ string extension,filename; // "/" rootdir? if(adres.length()==1) { filename="index.html"; extension="html"; filename=root_dir+filename; }else { /* * Get extension, number of variables, some pointers */ int qs_point=0; // adres of "?" int coma=0; // begining of extension no_variables=0; // number of http variables for(int i=0;i<=(signed int)adres.length();i++) { if(adres[i]=='?')qs_point=i; if((!no_variables)&&(adres[i]=='.'))coma=i; if(adres[i]=='&')no_variables++; }; if(!qs_point)qs_point=(adres.length()+1);; // in case of no "?" sign /* * Set filename and extension */ for(int i=(coma+1);i404 Error

File that you are looking for is not avaliable.

" << endl; cout << endl; return false; }; cout << "HTTP/1.1 200 OK" << endl; cout << "Server: " << banner << endl; cout << "Connection: close" << endl; /* * Checking Content-Type using "file" */ string arguments = "`which file` --mime-type -b "+filename; char mime_type[100]; FILE *file_program = popen(arguments.c_str(), "r"); fgets(mime_type,50,file_program); cout << "Content-Type: " << mime_type << endl; pclose(file_program); if(question!="GET") return true; /* * Send file! */ char znak; while(!plik.eof()) { plik.read(&znak,1); cout << znak; }; plik.close(); return true; }; /* * Signals? No problem. * */ void sigchld(int not_used) { int p_id; while((p_id = waitpid (-1, NULL, WNOHANG)) > 0); }; void sigkill(int not_used) { syslog(LOG_ERR,"daemon terminated using SIGKILL, PID=%d",getpid()); exit(EXIT_SUCCESS); }; void sigterm(int not_used) { syslog(LOG_ERR,"daemon terminated using SIGTERM, PID=%d",getpid()); exit(EXIT_SUCCESS); }; void sigusrone(int not_used) { }; /* * Let's do it! * main function. */ int main() { /* * Show welcome banner */ cout << "shttpd " << VERSION << " [build: " << __DATE__ << " " << __TIME__ << "]"; cout << " (" << AUTHOR << ")" << endl; /* * become a daemon */ if(!make_daemon()){syslog(LOG_ERR,"make_daemon() failed! Exiting.");exit(EXIT_FAILURE);}; // We are independent here. /* * Prepare network socket */ sock=socket(PF_INET, SOCK_STREAM, 0); if(sock<0) {syslog(LOG_ERR,"socket() failed!");exit(EXIT_FAILURE);} saddr.sin_family=PF_INET; saddr.sin_port=htons(port); saddr.sin_addr.s_addr=INADDR_ANY; /* * Bind, Listen */ if (bind(sock,(struct sockaddr *)&saddr,sizeof(saddr))) {syslog(LOG_ERR,"bind() on port %d failed!",port); exit(EXIT_FAILURE);}; if (listen (sock,max_conn)) {syslog(LOG_ERR,"listen() failed!"); exit(EXIT_FAILURE);}; /* * Main part */ while(true) // will work forever { /* * Accept new connection */ size_of_saddr = sizeof(saddr); sock_client = accept(sock, (struct sockaddr *)&client_addr,(socklen_t *) &size_of_saddr); syslog(LOG_NOTICE,"new connection accepted : %s",inet_ntoa (client_addr.sin_addr)); /* * Create new process for connection */ if((error_fork=fork())==0) { dup2 (sock_client, 0); dup2 (sock_client, 1); // close socket, but set as new STDIN,STDOUT,STDERR dup2 (sock_client, 2); /* * Let's talk with client ;-) */ exit(talk()); }; close(sock_client); }; exit(EXIT_SUCCESS); };