#include #include #include #include #include #include #include #include #include #include #include #include #define BUFLEN 1024 #define DOCROOT "/afs/informatik.uni-tuebingen.de/home/knauel/tmp/" #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\n" #define HTTP_NEWLINE "\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 PANIC(where) { perror(where); exit(-1); } #define WRITE_OR_CONT(sd, str, strlen) { if (write(sd, str, strlen) < 0) continue; } 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 decode_uri(uri, resbuf) char *uri, *resbuf; { int i = 0, j = 0; char c, code[3] = {NULL, NULL, NULL}; while ((c = uri[i++]) != NULL) { if (c == '%') if (isxdigit(uri[i]) && (uri[i+1] != 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 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 main(int argc, char *argv[]) { int sd, addrlen, csd = -1, port; struct sockaddr_in client_addr, local_addr; if (argc != 2) 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; } if (!((strcasecmp(method, HTTP_METHOD_GET) == 0) || (strcasecmp(method, HTTP_METHOD_HEAD) == 0))) { write(csd, HTTP_STATUS_505, strlen(HTTP_STATUS_505)); continue; } 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]; int nbytes; WRITE_OR_CONT(csd, HTTP_STATUS_200, strlen(HTTP_STATUS_200)); WRITE_OR_CONT(csd, HTTP_CONTENT_TYPE, strlen(HTTP_CONTENT_TYPE)); WRITE_OR_CONT(csd, HTTP_NEWLINE, strlen(HTTP_NEWLINE)); do { if ((nbytes = read(file, fbuf, BUFLEN)) < 0) PANIC("read"); WRITE_OR_CONT(csd, fbuf, nbytes); } while (nbytes == BUFLEN); } } close(sd); return 0; }