/*! \file httpd.c \brief This code is for a http deamon \author Matthew Toia \date 04-08-2004 */ /* this code is copyright of Matthew toia. * it was written to satisfy a programming * project and should not be used for anything * serious. */ #define MAXCONNECT 128 #include #include #include #include #include #include #include #include #include #include #include #include #include #include int init(int port); void * serve_page(void *arg); int parse_request(char *headers, char *path); void send_bad_request(int client); void send_error_response(int client); void sig_handler(int sig); void send_headers(int size, char *path, int client); /* Globals */ int server_sock; /*!\brief holds server socket, initialized in init*/ struct sockaddr_in local; /*!\brief holds local address, initialized in init*/ char *docroot; /*!\brief document root for server, initialized in main*/ int doclen; /*!\brief strlen of doc root, initialized in main*/ /*! \brief main forks a deamon process and returns \author Matthew Toia \date 04-08-2004 */ int main(int argc, char **argv) { pid_t pid; if(argc != 3) { printf("Usage: %s \n", argv[0]); return 1; } signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); docroot=argv[2]; doclen = strlen(docroot); if(docroot[doclen-1]=='/') { docroot[doclen-1]='/'; --doclen; } if((pid=fork())<0) { exit(1); } else if(pid!=0) { printf("PID: %d\n", pid); return 0; } /* set up a daemon proc */ setsid(); chdir("/"); umask(0); if(init(atoi(argv[1]))) { fprintf(stderr,"Could not initialize sockets\n"); exit(1); } while(1) { /* run forever, accept new connections, spawn thread to serve page */ int client_sock, *client, bufLen, error; struct sockaddr_in client_addr; pthread_t thread; /* accept incoming request */ client_sock = accept(server_sock, (struct sockaddr*) &client_addr, &bufLen); if(client_sock < 0) { fprintf(stderr,"Error accepting client\n"); continue; } client = (int *)malloc(sizeof(int)); memcpy(client, &client_sock, sizeof(int)); if(error=pthread_create(&thread, NULL, serve_page, client)) { fprintf(stderr,"Threading Error: %d",error); shutdown(server_sock, 2); exit(1); } } shutdown(server_sock, 2); return 0; } /*! * \author Matthew Toia * \brief Sets up server socket * \param port Port the server will run on * \return returns 0 on success, -1 on failure * \date 04-14-2004 */ int init(int port) { char name[1024]; struct hostent *host; int len; local.sin_family= AF_INET; local.sin_port= port; gethostname(name, 1024); host = gethostbyname(name); if(host==NULL) return -1; local.sin_addr.s_addr=*((unsigned long *)host->h_addr_list[0]); if((server_sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) return -1; if(bind(server_sock, (struct sockaddr*) &local, sizeof(struct sockaddr))) return -1; if(listen(server_sock, MAXCONNECT)) return -1; return 0; } /*! * \author Matthew Toia * \brief Workhorse function, sends response to client * \param arg Pointer to the client's socket * \return returns 0 on success, -1 on failure * \date 04-14-2004 */ void * serve_page(void *arg) { char buff[4096]; char path[4096]; struct stat sb; int theDoc, left, *client; void *mem, *data; client=(int *) arg; /* get headers and parse them, if not GET send bad request status */ recv(*client, buff, 4096, 0); if(parse_request(buff, path)) { send_bad_request(*client); return -1; } /* get info about requested file, send appropriate status */ stat(path, &sb); /* use default if path is a dir */ if(S_ISDIR(sb.st_mode)) { int len=strlen(path); /* add slash if necessary */ if(path[len-1]!='/') path[len-1] = '/'; /* buffer overflow guard */ if(len+10>4096) { send_bad_request(*client); return -1; } strcpy(path+len, "index.html"); } /* open doc and map it to memory */ theDoc = open(path, O_RDONLY, 0777); if(theDoc==-1&&sb.st_size==0) { send(*client, "HTTP/1.0 404 Not found\n" /*23*/ "Content-length: 130\n" /*20*/ "Content-type: text/html\n\n" /*25*/ "404 File Not Found"/*57*/ "

Error 404:


The requested file was not found" /*62*/ "\n", 204, 0); /*15*/ shutdown(*client, 2); return 0; } if(theDoc < 0) { /* serious error opening file */ send_error_response(*client); return -1; } mem=mmap(0, sb.st_size, PROT_READ, MAP_FILE, theDoc, 0); if((int)mem == -1) { fprintf(stderr,"Could not map file\n"); send_error_response(*client); return -1; } send_headers(sb.st_size, path, *client); data=mem; while((left=mem+sb.st_size-data) > 0) { if(left >= 512) send(*client, data, 512, 0); else send(*client, data, left, 0); data+=512; } if(munmap(mem, sb.st_size)) { fprintf(stderr,"Could not unmap file\n"); return -1; } if(close(theDoc)) { fprintf(stderr,"Could not close doc\n"); return -1; } shutdown(*client, 2); free(client); return 0; } /*! * \author Matthew Toia * \breif checks http request headers * \param headers a char buffer with the request * \param path a char buffer that will be filled the with translated path * \return returns 0 on success, -1 on failure * \date 04-14-2004 */ int parse_request(char *headers, char *path) { char *slice; if(strncmp("GET", headers, 3)) return -1; /* fill path buffer */ for(slice=headers+4; *slice > ' ';slice++); *slice='\0'; if(doclen+strlen(headers+4)+1 > 4096) /*long path causes overflow, send bad request response */ return -1; strcpy(path, docroot); if(*(headers+4)=='/') { strcpy(path+doclen, headers+4); } else { path[doclen]='/'; strcpy(path+doclen+1, headers+4); } return 0; } /*! * \author Matthew Toia * \brief Sends 400 status to client and closes socket * \param client a socket descriptor for the client * \date 04-14-2004 */ void send_bad_request(int client) { send(client, "HTTP/1.0 400 Bad Request\nContent-length: 0\n\n", 43, 0); shutdown(client, 2); } /*! * \author Matthew Toia * \brief Sends 500 status to client and closes socket * \param client a socket descriptor for the client * \date 04-14-2004 */ void send_error_response(int client) { send(client, "HTTP/1.0 500 Internal Server Error\nContent-length: 0\n\n", 54, 0); shutdown(client, 2); } /*! * \author Matthew Toia * \brief Sends 200 status and appropriate content headers * \param size the size of the content to be sent * \param path path to the content * \param client a socket descriptor for the client * \date 04-14-2004 */ void send_headers(int size, char *path, int client) { char *type, length[512]; int len=strlen(path); if(!strcmp(path+len-5, ".html")) { type="Content-type: text/html\n\n"; } else if(!strcmp(path+len-4, ".gif")) { type="Content-type: image/gif\n\n"; } else if(!strcmp(path+len-4, ".jpg") || !strcmp(path+len-5, ".jpeg")) { type="Content-type: image/jpg\n\n"; } else { type="Content-type: text/plain\n\n"; } sprintf(length, "Content-length: %d\n", size); send(client, "HTTP/1.0 200 OK\n", 16, 0); send(client, length, strlen(length), 0); send(client, type, strlen(type), 0); } /*! * \author Matthew Toia * \breif Handles signals by cleaning up server * \param sig The signal that is being handled * \date 04-14-2004 */ void sig_handler(int sig) { shutdown(server_sock, 2); exit(0); }