#include #include #include #include #include #include #include #include #include #include #include #include #define BUFLEN 1024 #define DOCROOT "/afs/informatik.uni-tuebingen.de/home/singh/tmp" #define WRITE 1 #define READ 0 #define HTTP_STATUS_200 "HTTP/1.1 200 OK\r\n" #define HTTP_STATUS_301 "HTTP/1.1 301 Moved permanently\r\n" #define HTTP_STATUS_400 "HTTP/1.1 400 Bad syntax\r\n" #define HTTP_STATUS_403 "HTTP/1.1 403 Forbidden\r\n" #define HTTP_STATUS_404 "HTTP/1.1 404 File not found\r\n" #define HTTP_STATUS_501 "HTTP/1.1 501 Internal server error\r\n" #define HTTP_STATUS_505 "HTTP/1.1 505 Method not implemented\r\n" #define HTTP_CONTENT_TYPE "Content-Type: text/plain\r\n" #define HTTP_NEWLINE "\r\n" #define HTTP_METHOD_GET "GET" #define HTTP_METHOD_HEAD "HEAD" #define REDIRECT_PATH_SRC "/geheim/" #define REDIRECT_PATH_DST "/topsecret/" #define REDIRECT_LOCATION "Location: " #define TEXT_NOTFOUND "File not found" #define TEXT_FORBIDDEN "Forbidden" #define FIND_ABS "/usr/bin/file" #define FIND "file" #define FIND_OPTIONS "-i" #define DU_ABS "/usr/bin/du" #define DU "du" #define DU_OPTIONS NULL #define LAST_CHUNK "0\r\n" #define CHUNKED_ENCODING "Transfer-Encoding: chunked\r\n" #define WRITE_OR_CONT(sd, str, strlen) { if (write(sd, str, strlen) < 0) continue; } int panic(char *where){ fprintf(stderr, "error at %s '%s'\n", where, strerror(errno)); exit(127); } int decode_uri(uri, resbuf) char *uri, *resbuf; { int i = 0, j = 0; char c, code[3] = {(char) NULL, (char) NULL, (char) NULL}; bzero((void *) resbuf, strlen(resbuf)); while ((c = uri[i++]) != (char) NULL) { if (c == '%') if (isxdigit(uri[i]) && (uri[i+1] != (char) NULL) && isxdigit(uri[i+1])) { strncpy(code, &uri[i], 2); resbuf[j++] = (char) strtol(code, NULL, 16); i += 2; } else return -1; else resbuf[j++] = c; } return 0; } int readline(int sd, char *buf, int buflen) { int n, rc; char c; for (n = 1; n < buflen; n++) { if ((rc = read(sd, &c, 1)) == 1) { *buf++ = c; if (c == '\n') break; } else if (rc == 0) { if (n == 1) return 0; else break; } else return -1; } *buf = 0; return n; } int request_line(line, method, uri, protver) char *line, *method, *uri, *protver; { char *t; if ((t = strtok(line, " ")) != NULL) strcpy(method, t); else return -1; if ((t = strtok(NULL, " ")) != NULL) strcpy(uri, t); else return -1; if ((t = strtok(NULL, " ")) != NULL) strcpy(protver, t); else return -1; return 0; } int exec_cmd(f_name, abs_cmd, cmd, options, res) char* f_name, *abs_cmd, *cmd, *options, *res; { int child_pid, pfd[2]; bzero((void *) res, strlen(res)); if( pipe(pfd) < 0) panic("pipe"); if((child_pid = fork()) < 0){ panic("fork"); }else if (child_pid == 0){ /*child process*/ close(pfd[READ]); /* stdout == write end of the pipe */ if(dup2(pfd[WRITE], WRITE) == -1 ) panic( "dup2 failed" ); close (pfd[WRITE]); if (options != NULL) execl(abs_cmd, cmd, options, f_name,0); else execl(abs_cmd, cmd, f_name,0); exit(0); } else { /*parent process*/ close(pfd[WRITE]); /* first close the write end of the pipe */ /* stdin == read end of the pipe */ if(dup2(pfd[READ], READ ) == -1 ) panic( "dup2 failed" ); close (pfd[READ]); readline( (int) stdin, res, BUFLEN); } return 0; } int main(int argc, char *argv[]) { int sd, addrlen, csd = -1, port; char chunk_size[BUFLEN] ; char tmp[BUFLEN]; struct sockaddr_in client_addr, local_addr; if (argc != 2){ printf("USEAGE: %s \n",argv[0] ); exit(-1); } port = atoi(argv[1]); if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) panic("socket"); bzero((void *) &local_addr, sizeof(struct sockaddr_in)); local_addr.sin_family = AF_INET; local_addr.sin_port = htons(port); local_addr.sin_addr.s_addr = INADDR_ANY; if (bind(sd, (struct sockaddr *) &local_addr, sizeof(struct sockaddr_in)) < 0) panic("bind"); if (listen(sd, SOMAXCONN) < 0) panic("listen"); while (1) { char request[BUFLEN], bogus[BUFLEN], method[BUFLEN]; char uri[BUFLEN], protver[BUFLEN], filename[2*BUFLEN]; int file, read_status; addrlen = sizeof(struct sockaddr); if (csd > 0) close(csd); printf("Waiting for connection\n"); csd = accept(sd, (struct sockaddr*) &client_addr, &addrlen); /* read the request line */ if (readline(csd, (char *) &request, BUFLEN) < 0) continue; /* read and ignore headers. */ /* A real HTTP implementation must take care of message body */ do { if ((read_status = readline(csd, (char *) &bogus, BUFLEN)) < 0) break; printf("%s", bogus); } while (index(bogus, '\r') != bogus); if (read_status < 0) continue; if (request_line(request, method, uri, protver) < 0) { write(csd, HTTP_STATUS_400, strlen(HTTP_STATUS_400)); continue; }else if (!((strcasecmp(method, HTTP_METHOD_GET) == 0) || (strcasecmp(method, HTTP_METHOD_HEAD) == 0))) { write(csd, HTTP_STATUS_505, strlen(HTTP_STATUS_505)); continue; } strcpy(tmp,uri); decode_uri(tmp,uri); strcpy(filename, DOCROOT); strcat(filename, uri); printf("Client requested: %s\n", filename); if (strncmp(uri, REDIRECT_PATH_SRC, strlen(REDIRECT_PATH_SRC)) == 0) { char dst[BUFLEN]; WRITE_OR_CONT(csd, HTTP_STATUS_301, strlen(HTTP_STATUS_301)); strcpy(dst, REDIRECT_LOCATION); strcat(dst, REDIRECT_PATH_DST); strcat(dst, &uri[strlen(REDIRECT_PATH_SRC)]); WRITE_OR_CONT(csd, dst, strlen(dst)); write(csd, HTTP_NEWLINE, strlen(HTTP_NEWLINE)); continue; } file = open(filename, O_RDONLY); if (file < 0) { switch (errno) { case EACCES: WRITE_OR_CONT(csd, HTTP_STATUS_403, strlen(HTTP_STATUS_403)); WRITE_OR_CONT(csd, HTTP_NEWLINE, strlen(HTTP_NEWLINE)); WRITE_OR_CONT(csd, TEXT_FORBIDDEN, strlen(TEXT_FORBIDDEN)); break; case ENOENT: WRITE_OR_CONT(csd, HTTP_STATUS_404, strlen(HTTP_STATUS_404)); WRITE_OR_CONT(csd, HTTP_NEWLINE, strlen(HTTP_NEWLINE)); WRITE_OR_CONT(csd, TEXT_NOTFOUND, strlen(TEXT_NOTFOUND)); break; default: panic("open"); break; } continue; } else { char fbuf[BUFLEN]; char res[BUFLEN]; char *tmp; char c_type_header[BUFLEN]; int nbytes; char c_length_header[BUFLEN]; WRITE_OR_CONT(csd, HTTP_STATUS_200, strlen(HTTP_STATUS_200)); /* create header with file size*/ exec_cmd(filename, DU_ABS, DU,DU_OPTIONS,res); tmp = strtok(res,"\t"); if(tmp != NULL){ strcpy(c_length_header,"Content-Length: "); strcat(c_length_header, tmp); WRITE_OR_CONT(csd, c_length_header, strlen(c_length_header)); WRITE_OR_CONT(csd, HTTP_NEWLINE, strlen(HTTP_NEWLINE)); } /* create header with content type*/ exec_cmd( filename, FIND_ABS, FIND, FIND_OPTIONS,res); tmp = strpbrk(res," "); if (tmp == NULL){ WRITE_OR_CONT(csd, HTTP_CONTENT_TYPE, strlen(HTTP_CONTENT_TYPE)); } else{ strcpy(c_type_header, "Content-Type:"); strcat(c_type_header, tmp); WRITE_OR_CONT(csd, c_type_header, strlen(c_type_header)); } if ((strcasecmp(method, HTTP_METHOD_GET) == 0)){ WRITE_OR_CONT(csd, CHUNKED_ENCODING, strlen(CHUNKED_ENCODING)); WRITE_OR_CONT(csd, HTTP_NEWLINE, strlen(HTTP_NEWLINE)); /* chunked transfer*/ do { if ((nbytes = read(file, fbuf, BUFLEN)) < 0) panic("read"); sprintf(chunk_size,"%x", nbytes); WRITE_OR_CONT(csd,chunk_size, strlen(chunk_size)); WRITE_OR_CONT(csd, HTTP_NEWLINE, strlen(HTTP_NEWLINE)); WRITE_OR_CONT(csd, fbuf, nbytes); WRITE_OR_CONT(csd, HTTP_NEWLINE, strlen(HTTP_NEWLINE)); } while (nbytes == BUFLEN); WRITE_OR_CONT(csd, LAST_CHUNK, strlen(LAST_CHUNK)); WRITE_OR_CONT(csd, HTTP_NEWLINE, strlen(HTTP_NEWLINE)); } } } close(sd); return 0; }