PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Tarea3/nweb_threads.c

http://github.com/jetm/Curso-Empotrado
C | 371 lines | 287 code | 60 blank | 24 comment | 106 complexity | 4d1fad0207c787a61d5ad0c86a0496ef MD5 | raw file
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <assert.h>
  5. #include <errno.h>
  6. #include <string.h>
  7. #include <fcntl.h>
  8. #include <signal.h>
  9. #include <sys/types.h>
  10. #include <sys/socket.h>
  11. #include <netinet/in.h>
  12. #include <arpa/inet.h>
  13. #include <pthread.h>
  14. #include <ctype.h>
  15. #define MAX_THREADS 500
  16. #define BUFSIZE 8096
  17. #define ERROR 42
  18. #define SORRY 43
  19. #define LOG 44
  20. #define MAX_PORT 60000
  21. #define THREAD_SLEEP 1
  22. #define MAX_SHELL 64
  23. void* attendClient(void* param);
  24. void Log(int type, char *s1, char *s2, int num);
  25. struct {
  26. char *ext;
  27. char *filetype;
  28. } extensions [] = {
  29. {"gif", "image/gif" },
  30. {"jpg", "image/jpeg"},
  31. {"jpeg","image/jpeg"},
  32. {"png", "image/png" },
  33. {"zip", "image/zip" },
  34. {"gz", "image/gz" },
  35. {"tar", "image/tar" },
  36. {"htm", "text/html" },
  37. {"html","text/html" },
  38. {"php", "text/html" },
  39. {"cgi", "text/html" },
  40. {0,0}
  41. };
  42. pthread_t* thrArray;
  43. int *descArr;
  44. int main(int argc, char **argv) {
  45. int i, ex, listenfd, socketfd, port = -1, thrAvail, xThreads = 10; // Default N Threads
  46. size_t length;
  47. static struct sockaddr_in cli_addr; /* static = initialised to zeros */
  48. static struct sockaddr_in serv_addr; /* static = initialised to zeros */
  49. static char *sPort;
  50. char *directory = NULL;
  51. if( argc == 2 && !strcmp(argv[1], "-?") ) {
  52. (void)printf("hint: nweb -p Port-Number -d Top-Directory -t Number-Threads\n\n"
  53. "\tnweb is a small and very safe mini web server\n"
  54. "\tnweb only servers out file/web pages with extensions named below\n"
  55. "\t and only from the named directory or its sub-directories.\n"
  56. "\tThere is no fancy features = safe and secure.\n\n"
  57. "\tExample: nweb 8181 /home/nwebdir &\n\n"
  58. "\tOnly Supports:");
  59. for (i=0; extensions[i].ext != 0; i++)
  60. (void)printf(" %s",extensions[i].ext);
  61. (void)printf("\n\tNot Supported: URLs including \"..\", Java, Javascript, CGI\n"
  62. "\tNot Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n"
  63. "\tNo warranty given or implied\n\tNigel Griffiths nag@uk.ibm.com\n");
  64. exit(0);
  65. }
  66. while ((i = getopt (argc, argv, "p:d:t:")) != -1)
  67. switch (i) {
  68. case 'p':
  69. port = atoi(optarg);
  70. if (port < 1 || port > MAX_PORT)
  71. Log(ERROR,"Invalid port number",optarg,0);
  72. sPort = optarg;
  73. break;
  74. case 'd':
  75. directory = optarg;
  76. if( !strncmp(directory,"/" ,2 ) || !strncmp(directory,"/etc", 5 ) ||
  77. !strncmp(directory,"/bin",5 ) || !strncmp(directory,"/lib", 5 ) ||
  78. !strncmp(directory,"/tmp",5 ) || !strncmp(directory,"/usr", 5 ) ||
  79. !strncmp(directory,"/dev",5 ) || !strncmp(directory,"/sbin",6) ){
  80. (void)printf("ERROR: Bad top directory %s, see nweb -?\n",directory);
  81. exit(3);
  82. }
  83. if(chdir(directory) == -1){
  84. (void)printf("ERROR: Can't Change to directory %s\n",directory);
  85. exit(4);
  86. }
  87. break;
  88. case 't':
  89. xThreads = atoi(optarg);
  90. if (xThreads < 1 || xThreads> MAX_THREADS )
  91. Log(ERROR,"Exceeded threads number",optarg,0);
  92. break;
  93. case '?':
  94. if (optopt == 'p' || optopt == 'd' || optopt == 't')
  95. fprintf (stderr, "Option -%c requires an argument.\n", optopt);
  96. else if (isprint (optopt))
  97. fprintf (stderr, "Unknown option `-%c'.\n", optopt);
  98. else
  99. fprintf (stderr,"Unknown option character `\\x%x'.\n", optopt);
  100. return 1;
  101. default:
  102. abort ();
  103. }
  104. if (port < 1)
  105. Log(ERROR,"Port not defined","",0);
  106. if (NULL == directory)
  107. Log(ERROR,"Directory not defined","",0);
  108. /* Become deamon + unstopable and no zombies children (= no wait()) */
  109. if (fork() != 0)
  110. return 0; /* parent returns OK to shell */
  111. (void)signal(SIGCLD, SIG_IGN); /* ignore child death */
  112. (void)signal(SIGHUP, SIG_IGN); /* ignore terminal hangups */
  113. //close open files
  114. for (i=0; i<32; i++)
  115. (void)close(i);
  116. // break away from process group
  117. (void)setpgrp();
  118. // if (sPort != '0')
  119. Log(LOG,"nweb starting",sPort,getpid());
  120. // Threads array
  121. thrArray = (pthread_t*) calloc (xThreads, sizeof(pthread_t));
  122. assert(thrArray != NULL);
  123. // Descriptors
  124. descArr = (int*)calloc (xThreads, sizeof(int));
  125. assert(descArr != NULL);
  126. // Create Threads
  127. for (i = 0; i < xThreads; i++) {
  128. descArr[i] = -1;
  129. if (pthread_create( &thrArray[i], NULL, &attendClient, &descArr[i] ) != 0)
  130. Log(ERROR,"system call","pthread_create",0);
  131. }
  132. // Setup the network socket
  133. if ((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0)
  134. Log(ERROR, "system call","socket",0);
  135. serv_addr.sin_family = AF_INET;
  136. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  137. serv_addr.sin_port = htons(port);
  138. if (bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0)
  139. Log(ERROR,"system call","bind",0);
  140. if (listen(listenfd,64) <0)
  141. Log(ERROR,"system call","listen",0);
  142. for (ex=1; ; ex++) {
  143. length = sizeof(cli_addr);
  144. if ((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)
  145. Log(ERROR,"system call","accept",0);
  146. thrAvail = -1;
  147. for(i = 0; i < xThreads; i++) {
  148. if (-1 == descArr[i]) {
  149. thrAvail = i;
  150. break;
  151. }
  152. }
  153. if (-1 == thrAvail) {
  154. Log(LOG,"All Threads're Busy","",0);
  155. (void)close(socketfd);
  156. } else {
  157. descArr[thrAvail] = socketfd;
  158. }
  159. }
  160. }
  161. void Log(int type, char *s1, char *s2, int num) {
  162. int fd;
  163. char logbuffer[BUFSIZE*2];
  164. switch (type) {
  165. case ERROR:
  166. (void)sprintf(logbuffer,"ERROR: %s:%s Errno=%d exiting pid=%d",s1, s2, errno,getpid());
  167. break;
  168. case SORRY:
  169. (void)sprintf(logbuffer, "<HTML><BODY><H1>nweb Web Server Sorry: %s %s</H1></BODY></HTML>\r\n", s1, s2);
  170. (void)write(num,logbuffer,strlen(logbuffer));
  171. (void)sprintf(logbuffer,"SORRY: %s:%s",s1, s2);
  172. break;
  173. case LOG:
  174. (void)sprintf(logbuffer," INFO: %s:%s:%d",s1, s2,num);
  175. break;
  176. }
  177. /* no checks here, nothing can be done a failure anyway */
  178. if((fd = open("nweb.log", O_CREAT| O_WRONLY | O_APPEND,0644)) >= 0) {
  179. (void)write(fd,logbuffer,strlen(logbuffer));
  180. (void)write(fd,"\n",1);
  181. (void)close(fd);
  182. }
  183. if(type == ERROR || type == SORRY) exit(3);
  184. }
  185. void* attendClient(void* param) {
  186. int *fd = (int*)param;
  187. int j, file_fd, buflen, len;
  188. long i, ret;
  189. char *fstr, *fext;
  190. char ok = 1;
  191. char buffer[BUFSIZE+1]; // static so zero filled
  192. FILE *fp = NULL; // file executed
  193. char shell[MAX_SHELL];
  194. while (1) {
  195. if (-1 == *fd) {
  196. sleep(THREAD_SLEEP);
  197. } else {
  198. ok = 1;
  199. //read Web request in one go
  200. ret = read(*fd,buffer,BUFSIZE);
  201. if(ret == 0 || ret == -1) {
  202. Log(LOG,"failed to read browser request","",*fd); // read failure stop now
  203. ok = 0;
  204. }
  205. if (ok) {
  206. // Return code is valid chars
  207. if(ret > 0 && ret < BUFSIZE)
  208. buffer[ret]=0; //terminate the buffer
  209. else
  210. buffer[0]=0;
  211. for(i=0; i<ret; i++) /* remove CF and LF characters */
  212. if(buffer[i] == '\r' || buffer[i] == '\n')
  213. buffer[i]='*';
  214. if( strncmp(buffer,"GET ",4) && strncmp(buffer,"get ",4) ) {
  215. Log(LOG,"Only simple GET operation supported",buffer,*fd);
  216. ok = 0;
  217. }
  218. }
  219. if (ok) {
  220. // null terminate after the second space to ignore extra stuff
  221. for(i=4; i<BUFSIZE; i++) {
  222. if(buffer[i] == ' ') { /* string is "GET URL " +lots of other stuff */
  223. buffer[i] = 0;
  224. break;
  225. }
  226. }
  227. // check for illegal parent directory use ..
  228. for(j=0; j<i-1; j++)
  229. if(buffer[j] == '.' && buffer[j+1] == '.') {
  230. Log(LOG,"Parent directory (..) path names not supported",buffer,*fd);
  231. ok = 0;
  232. break;
  233. }
  234. }
  235. if (ok) {
  236. // convert no filename to index file
  237. if( !strncmp(&buffer[0],"GET /\0",6) || !strncmp(&buffer[0],"get /\0",6) )
  238. (void)strcpy(buffer,"GET /index.html");
  239. // work out the file type and check we support it
  240. buflen=strlen(buffer);
  241. fstr = (char *)0;
  242. fext = (char *)0;
  243. for (i=0; extensions[i].ext != 0; i++) {
  244. len = strlen(extensions[i].ext);
  245. if (!strncmp(&buffer[buflen-len], extensions[i].ext, len)) {
  246. fstr = extensions[i].filetype;
  247. fext = extensions[i].ext;
  248. //Log(LOG,"HH- ",fext,*fd);
  249. break;
  250. }
  251. }
  252. if (fstr == 0) {
  253. Log(LOG,"file extension type not supported",buffer,*fd);
  254. ok = 0;
  255. }
  256. }
  257. if (ok) {
  258. // open the file for reading
  259. // CGI Execution
  260. if (!strncmp(extensions[i].ext, "cgi", 3)) {
  261. strcat(shell,"./");
  262. strcat(shell,&buffer[5]);
  263. Log(LOG,"CGI Script", shell, *fd);
  264. if ((fp = popen(shell,"r")) == NULL) {
  265. Log(LOG, "failed to open file",&buffer[5],*fd);
  266. ok = 0;
  267. }
  268. // PHP Execution
  269. } else if (!strncmp(extensions[i].ext, "php", 3)) {
  270. strcat(shell,"php -f ");
  271. strcat(shell,&buffer[5]);
  272. Log(LOG,"PHP Script", shell, *fd);
  273. if ((fp = popen(shell,"r")) == NULL) {
  274. Log(LOG, "failed to open file",&buffer[5],*fd);
  275. ok = 0;
  276. }
  277. } else {
  278. if ((file_fd = open(&buffer[5],O_RDONLY)) == -1) {
  279. Log(LOG, "failed to open file",&buffer[5],*fd);
  280. ok = 0;
  281. }
  282. }
  283. }
  284. if (ok) {
  285. (void)sprintf(buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr);
  286. (void)write(*fd,buffer,strlen(buffer));
  287. // send file in 8KB block - last block may be smaller
  288. // CGI or PHP
  289. if (!strncmp(extensions[i].ext, "cgi", 3) ||
  290. !strncmp(extensions[i].ext, "php", 3)) {
  291. while (fgets(buffer, BUFSIZE, fp)) {
  292. (void)write(*fd, buffer, strlen(buffer));
  293. }
  294. // Clear shell Array
  295. memset((void*)&shell, 0, sizeof(int)*MAX_SHELL);
  296. pclose(fp);
  297. } else {
  298. while ((ret = read(file_fd, buffer, BUFSIZE)) > 0) {
  299. (void)write(*fd, buffer, ret);
  300. }
  301. close(file_fd);
  302. }
  303. //to allow socket to drain
  304. sleep(1);
  305. }
  306. //Make the thread available again
  307. (void)close(*fd);
  308. *fd = -1;
  309. }
  310. }
  311. }