chttpd.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. // CTHTTPD - Simple Web Server - GPLv2
  2. // Chris Dorman, 2012-2014 (cddo@riseup.net)
  3. // Help from Nickolai Vurgaft (slipperygate@gmail.com)
  4. #include "chttpd.h"
  5. #include "functions.h"
  6. #include "mimetypes.h"
  7. #include "check.h"
  8. #include "cgi.h"
  9. #include "dep.h"
  10. #include "log.h"
  11. const char *client = "chttpd";
  12. const char *version = "1.3.1b";
  13. const char *sys_lable = "Linux";
  14. int forward_slash = 47; // forward slash in ascii
  15. #define CONFBUF 1024
  16. char *equal = "=";
  17. struct config
  18. {
  19. char htdocs[CONFBUF];
  20. char port[CONFBUF];
  21. char status[CONFBUF];
  22. char cgi[CONFBUF];
  23. char maxspeed[CONFBUF];
  24. };
  25. // write to struct
  26. struct config get_config(char *filename)
  27. {
  28. struct config configstruct;
  29. // open config as readable
  30. FILE *file = fopen (filename, "r");
  31. // check if config opens successfully
  32. if( access( filename, F_OK ) == -1 ) {
  33. memcpy(configstruct.status,"1",1);
  34. }
  35. else
  36. {
  37. memcpy(configstruct.status,"0",1);
  38. }
  39. // if file is null, end
  40. if (file != NULL)
  41. {
  42. // line buffer for config
  43. char line[CONFBUF];
  44. // int used to track config line
  45. //int i = 0;
  46. // config while loop, loops fgets until end of file
  47. while(fgets(line, sizeof(line), file) != NULL)
  48. {
  49. char *cfline; // setup string
  50. cfline = strtok(line, equal);
  51. // if line is commented out, skip
  52. if (strncmp("#",line,1)==0)
  53. continue;
  54. if (strncmp("HTDOCS",cfline,6)==0 || strncmp("htdocs",cfline,6)==0) {
  55. cfline = strtok(NULL, equal); // call strtok to get value
  56. // if newline is found, remove newline from string
  57. if(cfline[strlen(cfline)-1] == '\n')
  58. cfline[strlen(cfline)-1] = 0;
  59. // write htdocs path to struct
  60. memcpy(configstruct.htdocs,cfline,strlen(cfline));
  61. } else if (strncmp("PORT",cfline,4)==0 || strncmp("port",cfline,4)==0){
  62. cfline = strtok(NULL, equal); // call strtok to get value
  63. // if newline is found, remove newline from string
  64. if(cfline[strlen(cfline)-1] == '\n')
  65. cfline[strlen(cfline)-1] = 0;
  66. // write port to struct
  67. memcpy(configstruct.port,cfline,strlen(cfline));
  68. } else if (strncmp("ENABLE_CGI",cfline,10)==0 || strncmp("enable_cgi",cfline,10)==0){
  69. cfline = strtok(NULL, equal); // call strtok to get value
  70. // if newline is found, remove newline from string
  71. if(cfline[strlen(cfline)-1] == '\n')
  72. cfline[strlen(cfline)-1] = 0;
  73. // write cgi status to struct
  74. memcpy(configstruct.cgi,cfline,strlen(cfline));
  75. } else if (strncmp("MAX_SEND_SPEED",cfline,14)==0 || strncmp("max_send_speed",cfline,10)==0){
  76. cfline = strtok(NULL, equal); // call strtok to get value
  77. // if newline is found, remove newline from string
  78. if(cfline[strlen(cfline)-1] == '\n')
  79. cfline[strlen(cfline)-1] = 0;
  80. // write cgi status to struct
  81. memcpy(configstruct.maxspeed,cfline,strlen(cfline));
  82. }
  83. } // End while
  84. } // End if file
  85. fclose(file);
  86. return configstruct;
  87. }
  88. void web(int fd, int hit, char *datadir, char *cgistatus, char *throttle_speed)
  89. {
  90. int j, file_fd, buflen, len, contentfs;
  91. long i, filesize;
  92. char *fstr;
  93. //char *exten;
  94. char *path;
  95. char *protocol;
  96. char *stripslash_index;
  97. char *stripslash_path;
  98. size_t pathlen;
  99. static char buffer[BUFSIZE+1];
  100. static char listbuffer[LIST_BUFSIZE*2];
  101. // Check to see if file is corrupted
  102. filesize = read(fd,buffer,BUFSIZE);
  103. if(filesize == 0 || filesize == -1) {
  104. do_chttpd_log(SORRY,"failed to read browser request","",fd);
  105. }
  106. if(filesize > 0 && filesize < BUFSIZE) {
  107. buffer[filesize]=0;
  108. } else {
  109. buffer[0]=0;
  110. }
  111. for(i=0;i<filesize;i++) {
  112. if(buffer[i] == '\r' || buffer[i] == '\n') {
  113. buffer[i]='*';
  114. }
  115. }
  116. do_chttpd_log(LOG,"request",buffer,hit);
  117. if(strncmp(buffer,"GET ",4) && strncmp(buffer,"get ",4)) {
  118. do_chttpd_log(SORRY,"Only simple GET operation supported",buffer,fd);
  119. }
  120. for(i=4;i<BUFSIZE;i++) {
  121. if(buffer[i] == ' ') {
  122. buffer[i] = 0;
  123. break;
  124. }
  125. }
  126. for(j=0;j<i-1;j++)
  127. if(buffer[j] == '.' && buffer[j+1] == '.')
  128. do_chttpd_log(SORRY,"Parent directory (..) path names not supported",buffer,fd);
  129. if(!strncmp(&buffer[0],"GET /\0",6) || !strncmp(&buffer[0],"get /\0",6)) {
  130. if(file_exists("index.html") == 0) {
  131. strcpy(buffer,"GET /index.html");
  132. } else {
  133. DIR *d = opendir(".");
  134. struct dirent* dirp; // struct dirp for directory listing
  135. sprintf(listbuffer,"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
  136. write(fd,listbuffer,strlen(listbuffer)); // write header socket
  137. sprintf(listbuffer,"<!DOCTYPE html>\r\n"
  138. "<html>\r\n"
  139. "<head>\r\n"
  140. "\t<title>Directory listing of /</title>\r\n"
  141. "</head>\r\n"
  142. "<body>\r\n"
  143. "\t<h2>Directory listing of /</h2>\r\n"
  144. "\t<hr />\r\n<table>\r\n");
  145. write(fd,listbuffer,strlen(listbuffer)); // write list html to socket
  146. // There is no parent directory at the root of the web servers filesystem xD
  147. //sprintf(listbuffer,"\t<tr><td><a href=\"..\">Parent Directory</a></td></tr>\r\n");
  148. //write(fd,listbuffer,strlen(listbuffer));
  149. // Start listing files and directories
  150. while ((dirp = readdir(d)))
  151. {
  152. if (dirp->d_name[0] == '.')
  153. continue;
  154. sprintf(listbuffer,"\t<tr><td><a href=\"%s\">%s</a></td></tr>\r\n", dirp->d_name, dirp->d_name);
  155. write(fd,listbuffer,strlen(listbuffer));
  156. }
  157. sprintf(listbuffer,"\t</table>\r\n<hr /><address>%s %s (%s)</address>\r\n</body>\r\n</html>\r\n", client, version, sys_lable);
  158. write(fd,listbuffer,strlen(listbuffer));
  159. exit(0);
  160. }
  161. }
  162. // set uri path
  163. path = fixpath(strchr(buffer,' '));
  164. path++;
  165. // get protocol
  166. protocol = strchr(path,' ');
  167. protocol++;
  168. pathlen = strlen(path);
  169. if(is_dir(path) == 1) {
  170. if(path[pathlen - 1] != forward_slash) // if there is no "/" at the end of the url, add it
  171. {
  172. strcat(path,"/");
  173. sprintf(listbuffer,"HTTP/1.0 301 Moved Permanently\r\nLocation: %s\r\n\r\n", path); //header to buffer
  174. write(fd,listbuffer,strlen(listbuffer)); // write header to socket
  175. //sprintf(listbuffer,"<html><meta http-equiv=\"refresh\" content=\"0;url=%s\"></html>",path);
  176. //write(fd,listbuffer,strlen(listbuffer)); // write redirect
  177. exit(0); // stop here, let the browser reconnect with a new url
  178. }
  179. }
  180. // Check if directory was requested, if so, send index.html
  181. if (is_dir(path) == 1) {
  182. char getindex[PATH_MAX];
  183. strcpy(getindex,path);
  184. strcat(getindex,"index.html");
  185. stripslash_index = getindex + 1; // directory + index (for index redirection)
  186. stripslash_path = path + 1; // get full path
  187. if(file_exists(stripslash_index) != 0)
  188. {
  189. DIR *d = opendir(stripslash_path);
  190. struct dirent* dirp; // struct dirp for directory listing
  191. sprintf(listbuffer,"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
  192. write(fd,listbuffer,strlen(listbuffer)); // write header socket
  193. sprintf(listbuffer,"<!DOCTYPE html>\r\n"
  194. "<html>\r\n"
  195. "<head>\r\n"
  196. "\t<title>Directory listing of %s</title>\r\n"
  197. "</head>\r\n"
  198. "<body>\r\n"
  199. "\t<h2>Directory listing of %s</h2>\r\n"
  200. "\t<hr />\r\n<table>\r\n", path, path);
  201. write(fd,listbuffer,strlen(listbuffer)); // write list html to socket
  202. sprintf(listbuffer,"\t<tr><td><a href=\"..\">Parent Directory</a></td></tr>\r\n");
  203. write(fd,listbuffer,strlen(listbuffer));
  204. // Start listing files and directories
  205. while ((dirp = readdir(d)))
  206. {
  207. if (dirp->d_name[0] == '.')
  208. continue;
  209. sprintf(listbuffer,"\t<tr><td><a href=\"%s\">%s</a></td></tr>\r\n", dirp->d_name, dirp->d_name);
  210. write(fd,listbuffer,strlen(listbuffer));
  211. }
  212. sprintf(listbuffer,"\t</table>\r\n<hr /><address>%s %s (%s)</address>\r\n</body>\r\n</html>\r\n", client, version, sys_lable);
  213. write(fd,listbuffer,strlen(listbuffer));
  214. exit(0);
  215. }
  216. else
  217. {
  218. strcat(path,"index.html");
  219. }
  220. }
  221. // Check file extensions and mime types before sending headers
  222. buflen=strlen(buffer);
  223. fstr = (char *)0;
  224. //exten = (char *)0;
  225. for(i=0;extensions[i].ext != 0;i++) {
  226. len = strlen(extensions[i].ext);
  227. if( !strncmp(&buffer[buflen-len], extensions[i].ext, len)) {
  228. fstr = extensions[i].filetype;
  229. //exten = extensions[i].ext;
  230. break;
  231. }
  232. }
  233. if(fstr == 0) {
  234. fstr = "application/octet-stream";
  235. }
  236. if(strncmp("serverlog",fstr,9)==0) do_chttpd_log(SORRY,"Cannot retrieve server logs, forbidden!",buffer,fd);
  237. if(( file_fd = open(&path[1],O_RDONLY)) == -1) {
  238. do_chttpd_log(SORRY, "failed to open file",&path[1],fd);
  239. }
  240. if(strncmp("yes",cgistatus,3)==0) {
  241. if(strncmp("servercgi",fstr,9)==0) {
  242. do_cgi(path,fd,datadir);
  243. exit(0);
  244. }
  245. }
  246. else
  247. {
  248. if(strncmp("servercgi",fstr,9)==0) {
  249. do_chttpd_log(SORRY, "CGI disabled - ", "Cannot access CGI script", fd);
  250. }
  251. }
  252. struct stat filesz;
  253. stat(&path[1], &filesz);
  254. contentfs = filesz.st_size;
  255. do_chttpd_log(LOG,"SEND",&path[1],hit);
  256. sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n", fstr);
  257. write(fd,buffer,strlen(buffer));
  258. // Add content length to http header
  259. sprintf(buffer,"Content-Length: %d\r\n\r\n", contentfs);
  260. write(fd,buffer,strlen(buffer));
  261. int time_ms, bufchunk, limit, dothrottle;
  262. if(strncmp("0",throttle_speed,1)!=0) {
  263. limit = atoi(throttle_speed);
  264. bufchunk = 4096;
  265. time_ms = 1000/(limit/bufchunk);
  266. if(time_ms<1) {
  267. dothrottle = 0;
  268. } else {
  269. dothrottle = 1;
  270. }
  271. } else {
  272. dothrottle = 0;
  273. }
  274. if(dothrottle == 1) {
  275. while((filesize = read(file_fd, buffer, BUFSIZE)) > 0) {
  276. ms_sleep(time_ms);
  277. write(fd,buffer,filesize);
  278. }
  279. }
  280. else
  281. {
  282. while((filesize = read(file_fd, buffer, BUFSIZE)) > 0) {
  283. write(fd,buffer,filesize);
  284. }
  285. }
  286. #ifdef LINUX
  287. sleep(1);
  288. #endif
  289. exit(1);
  290. }
  291. int main(int argc, char **argv)
  292. {
  293. int i, port, pid, listenfd, socketfd, hit;
  294. socklen_t length;
  295. static struct sockaddr_in cli_addr;
  296. static struct sockaddr_in serv_addr;
  297. struct config configstruct; // config struct
  298. if(argc > 2 || argc < 2 || !strcmp(argv[1], "-?") || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
  299. printf("usage: chttpd [chttpd config] &\n"
  300. "Example: chttpd /path/to/config.conf &\n");
  301. exit(0); // give exit code error
  302. }
  303. if(argc == 2) {
  304. configstruct = get_config(argv[1]);
  305. if(atoi(configstruct.status) == 1) {
  306. printf("ERROR: Can't find configuration file at %s.\n", argv[1]);
  307. exit(1); // give exit code error
  308. }
  309. }
  310. //
  311. // Parse the config file
  312. //
  313. if(chdir(configstruct.htdocs) == -1) {
  314. printf("Warning: failed to chdir Errno: %d\n", errno);
  315. printf("Warning: Failed to set htdocs value: %s\n", configstruct.htdocs);
  316. exit(1);
  317. }
  318. if(fork() != 0)
  319. return 1;
  320. signal(SIGCLD, SIG_IGN);
  321. signal(SIGHUP, SIG_IGN);
  322. for(i=0;i<32;i++)
  323. close(i);
  324. setpgrp();
  325. port = (int) strtol(configstruct.port, NULL, 0);
  326. do_chttpd_log(LOG,"CHTTPD server starting",configstruct.port,getpid());
  327. if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0) {
  328. do_chttpd_log(ERROR, "system call","socket",0);
  329. }
  330. if(port < 0 || port > 60000) {
  331. do_chttpd_log(ERROR,"Invalid port number try [1,60000], tried starting on ",configstruct.port,0);
  332. }
  333. bzero(&serv_addr, sizeof(serv_addr));
  334. serv_addr.sin_family = AF_INET;
  335. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  336. serv_addr.sin_port = htons(port);
  337. if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0) {
  338. do_chttpd_log(ERROR,"Failed to ","bind",0);
  339. }
  340. if( listen(listenfd,64) <0) {
  341. do_chttpd_log(ERROR,"Failed to","listen",0);
  342. }
  343. for(hit=1; ;hit++) {
  344. length = sizeof(cli_addr);
  345. if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, (socklen_t*) &length)) < 0) {
  346. do_chttpd_log(ERROR,"Failed to","accept",0);
  347. }
  348. if((pid = fork()) < 0) {
  349. do_chttpd_log(ERROR,"Failed to","fork",0);
  350. } else {
  351. if(pid == 0) {
  352. close(listenfd);
  353. web(socketfd,hit,configstruct.htdocs,configstruct.cgi,configstruct.maxspeed);
  354. } else {
  355. close(socketfd);
  356. }
  357. }
  358. }
  359. }