/* * MicroHTTPD - A minimalistic HTTP daemon. * * Written 2001 by Thunder from the hill */ /* * Include files */ #include #include #include #include #include #include #include #include #include #include /* * Functions */ int sendfile(int Socket, char *filename); /* Send file */ int http_request(int Socket, char *req); /* Respond to HTTP request */ void clear(char *set, size_t len); /* Emtpy array of size len */ /* * Variables */ int MySock; /* Socket */ int NewSock; /* Socket for accept. */ int peer_size = 0; /* Name stack size for the peer */ struct sockaddr_in addr; /* Our address */ struct sockaddr_in peer; /* The peer's address */ struct hostent *hostentry; /* Hostentry - for anything we need */ char *request; /* Our HTTP request */ char *BufIn, *BufOut; /* Input/Output-Buffer */ int BufInLen = 2048, BufOutLen = 2048; struct arx { short port; char *basedir; } args; void clear(char *set, size_t len) { char *sptr; realloc(set, len + 1); sptr = set; for (sptr = set; sptr < set + len; sptr++) { *sptr = '\0'; } return; } int http_request(int Socket, char *req) { char *req_substr; req_substr = strtok(req, " "); printf("HTTP Request: %s", req_substr); if (strlen(req_substr) > 0 && strcmp(req_substr, "GET") == 0) { req_substr = strtok(NULL, " "); printf(" %s\n", req_substr); /* if (strcmp(req_substr, "/") == 0) { if (sendfile(Socket, "/index.html") < 0) { printf("Error while sending %s in line %i!\n", req_substr, __LINE__ - 1); return -1; } } else */ if (strstr(req_substr, "/internal/") != NULL) { if (strcmp(req_substr, "/internal/uhttpd-logo") == 0) { char uhttpd_logo[] = {71, 73, 70, 56, 57, 97, 100, 0, 25, 0, -9, 0, 0, 0, 0, -128, 124, 124, -67, -102, -102, -51, -31, -31, -16, -23, -23, -12, -16, -16, -9, -8, -8, -5, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, 0, 100, 0, 25, 0, 71, 8, -4, 0, 1, 8, 28, 72, -80, -96, -63, -125, 8, 19, 42, 92, -56, -80, -95, -61, -127, 5, 34, 70, 76, 56, 96, 34, -128, -118, 22, 21, 22, 104, -72, -15, 96, 71, -126, 31, 25, -122, 124, 88, 48, 35, -56, -111, 26, 81, -110, 116, -88, 114, -91, -53, -105, 48, 99, -54, 76, 104, 114, 102, -53, -103, 56, 89, 74, -108, 88, 50, 100, 77, -113, 39, 81, 126, -12, 105, -112, -88, -64, -114, 67, 115, -98, 124, -8, 83, -87, 83, -105, 60, 87, -18, 124, 74, -107, 41, -43, -90, 85, -77, 70, 45, -22, -13, 38, -60, -98, 38, 45, 26, 5, 73, -10, -88, -39, -77, 76, -57, 114, -35, -71, -43, -93, -41, -81, 104, -113, -114, 20, -37, 118, 106, -50, -102, 88, -117, 102, -35, -5, -78, 109, 79, -66, -128, 117, -78, 29, 60, 56, -80, 97, -82, 56, -13, 30, -66, 123, 117, -79, 86, -118, 22, 49, -66, 5, 48, -103, 50, 66, -95, 86, 97, 42, 14, -5, -106, -16, 101, -54, 93, 51, -38, -75, 43, 87, 113, 80, -62, 126, 65, 47, 93, -117, -70, -80, -27, -72, -104, -31, -66, 78, 42, -107, -76, -37, 0, 0, 4, 96, 92, -24, 26, 40, 108, -67, -78, -111, -54, -42, -36, -110, 109, -128, -120, 3, 104, 86, -106, -71, -100, -71, -48, -26, -51, 29, 59, -1, 88, 81, 100, 116, -23, 119, 77, -85, -58, -50, 29, 98, -21, -21, 4, -35, 11, 6, 4, 0, 33, -2, -45, 84, 104, 105, 115, 32, 102, 105, 108, 101, 32, 119, 97, 115, 32, 99, 114, 101, 97, 116, 101, 100, 32, 98, 121, 13, 13, 71, 114, 97, 112, 104, 105, 99, 32, 87, 111, 114, 107, 115, 104, 111, 112, -103, 32, 80, 114, 111, 102, 101, 115, 115, 105, 111, 110, 97, 108, 32, 50, 46, 48, 97, 13, 13, 102, 114, 111, 109, 32, 65, 108, 99, 104, 101, 109, 121, 32, 77, 105, 110, 100, 119, 111, 114, 107, 115, 32, 73, 110, 99, 46, 13, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 109, 105, 110, 100, 119, 111, 114, 107, 115, 104, 111, 112, 46, 99, 111, 109, 13, 13, 84, 104, 105, 115, 32, 105, 109, 97, 103, 101, 32, 109, 97, 121, 32, 104, 97, 118, 101, 32, 98, 101, 101, 110, 32, 99, 114, 101, 97, 116, 101, 100, 32, 98, 121, 13, 97, 32, 112, 97, 114, 116, 121, 32, 111, 116, 104, 101, 114, 32, 116, 104, 97, 110, 32, 65, 108, 99, 104, 101, 109, 121, 32, 77, 105, 110, 100, 119, 111, 114, 107, 115, 32, 73, 110, 99, 46, 13, 13, 85, 115, 101, 32, 110, 111, 32, 104, 111, 111, 107, 115, 0, 59, -1, '\0'}; clear(BufOut, BufOutLen); if (BufOutLen < 1536 || BufOutLen > 2560) { realloc(BufOut, 1536); BufOutLen = 1536; } strcpy(BufOut, uhttpd_logo); } else if (strcmp(req_substr, "/internal/uhttpd-version") == 0) { clear(BufOut, BufOutLen); if (BufOutLen < 2304 || BufOutLen > 2560) { realloc(BufOut, 2304); BufOutLen = 2304; } strcpy(BufOut, "\n\n \n The MicroHTTPD\n \n \n \n \n
\"uhttpdWant something tiny at home instead of huge HTTP servers? We have it: MicroHTTPD
\n

MicroHTTPD

\n

The MicroHTTPD (or evern more short, uhttpd) is a very small OpenSource HTTP daemon. CGIs are not yet supported, but may come later. If you like it, please tell the author!\n

License

\n

MicroHTTPD v1.0 - a minimalistic HTTP daemon.
\n Copyright (c) 2001, Thunder from the hill.

\n This server is free software; you can redistribute and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) any later version.

\n This server is distributed in hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

\n For the license, visit the GNU website!


\n Thunder from the hill hereby disclaims any copyright interest in the server »MicroHTTPD« (a minimalistic web server), written by Thunder from the hill.
thunder, April 28, 2001
Thunder from the hill, OpenSource Author\n

Visit this site for updates and news!

\n \n\n"); } else if (strcmp(req_substr, "/internal/getuhttpdversion") == 0) { clear(BufOut, BufOutLen); if (BufOutLen < 16 || BufOutLen > 128) { realloc(BufOut, 16); BufOutLen = 16; } strcpy(BufOut, "1.0\n"); } else { clear(BufOut, BufOutLen); if (BufOutLen < 16 || BufOutLen > 128) { realloc(BufOut, 16); BufOutLen = 16; } strcpy(BufOut, "HTTP/1.0 404\n"); printf("Answered 404\n"); } if (send(Socket, BufOut, BufOutLen, 0) < strlen(BufOut)) { printf("Error while sending %i bytes in line %i!\n", strlen(BufOut), __LINE__ - 1); return -1; } } else { /* req_substr = strtok(NULL, " "); */ if (sendfile(Socket, req_substr) < 0) { printf("Error while sending %s in line %i!\n", req_substr, __LINE__ - 1); return -1; } } } else { printf("Invalid request method %s.\n", req_substr); strcpy(BufOut, "\n\n \n Invalid request method\n \n \n

Invalid request method

\n

The desired request method was not supported by the server.

\n \n\n"); if (send(Socket, BufOut, 1024, 0) < 0) { printf("Error while sending 318 bytes in line %i.\n", __LINE__ - 1); return -1; } } return 0; } int sendfile(int Socket, char *filename) { int fd; /* File descriptor */ char *NoQuestPtr; /* Question marks removed */ char fn_afterall[strlen(filename) + 20]; /* What will be left from filename */ char sbuf[1024]; int i = 0; struct stat _thisfile; /* Stat structure */ struct stat *thisfile = &_thisfile; /* Stat structure pointer */ NoQuestPtr = strtok(filename, "?"); if (stat(NoQuestPtr, thisfile) < 0) { if (send(Socket, "HTTP/1.0 404\n", 14, 0) < 0) { return -1; } return 0; } strcpy(fn_afterall, NoQuestPtr); if (S_ISDIR(thisfile->st_mode) != 0) { char *fn_last = NoQuestPtr + strlen(NoQuestPtr) - 1; printf("%s considered to be a directory (%i)\n", NoQuestPtr, S_ISDIR(thisfile->st_mode)); /* realloc(fn_afterall, strlen(filename) + 20); */ printf("STUB: realloc() segfaults in sendfile(Socket, \"%s\");\n", NoQuestPtr); printf("fn_afterall has address %p and contains string %s.\n", fn_afterall, fn_afterall); if (*fn_last != '/') strcat(fn_afterall, "/"); strcat(fn_afterall, "index.html"); } if ((fd = open(fn_afterall, O_RDONLY)) < 0) { printf("Access to %s seems denied.\n", fn_afterall); if (send(Socket, "\n\n \n Access to file denied.\n \n \n

File access error

\n

File access seems denied: chdir() worked, stat() returned OK, but file access seems denied.

\n \n\n", 347, 0) < 0) { return -1; } return 0; } printf("sbuf has address %p and contains %s. Clearing.\n", sbuf, sbuf); for (i = 0; i < 1024; i++) sbuf[i] = '\0'; while (read(fd, sbuf, 1024) > 0) { if (send(Socket, sbuf, strlen(sbuf), 0) < 0) { return -1; } } if (close(fd) < 0) { printf("Error while closing file descriptor!\n"); } if (chdir("/") < 0) { printf("Error when changing back to base dir (/).\n"); } return 0; } void usage(void) { printf("%s [-d directory] port\n\t-d directory\n\t\tThe web directory (target for chroot).\n\tport\n\t\tThe port to bind the server to.\n", __FILE__); return; } int main (int argc, char **argv) { int argp = 1; BufOut = malloc(2048); BufIn = malloc(2048); args.basedir = malloc(24); strcpy(args.basedir, "/usr/local/httpd"); if (argc < 2 || argc > 4) { usage(); exit (1); } while (argp < argc) { if (strcmp(argv[argp], "-d") == 0) { if (argv[argp + 1] != NULL) { clear(args.basedir, strlen(args.basedir)); realloc(args.basedir, strlen(argv[argp + 1]) + 1); strcpy(args.basedir, argv[argp + 1]); argp++; argp++; } else { printf("Parameter -d requires a directory to be set.\n"); usage(); exit(1); } } else { args.port = (short) atol(argv[argp]); argp++; } } printf("\033[2J\t\t\t --- MicroHTTPD ---\n"); printf("Creating socket: AF_INET, SOCK_STREAM, IPPROTO_TCP.\n"); MySock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (MySock == (~0)) { printf("%s: Error creating server socket.\nErrno was: %i\n", __FILE__, errno); exit(1); } printf("Setting up server socket: "); addr.sin_family = AF_INET; printf("AF_INET"); addr.sin_port = htons(args.port); printf(", Port=%i", args.port); addr.sin_addr.s_addr = htonl(INADDR_ANY); printf(", any IP.\n"); if (bind(MySock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { printf("%s: Error when using bind.\n", __FILE__); if (errno == EADDRNOTAVAIL) { printf("Address not available.\n"); } else if (errno == EADDRINUSE) { printf("Some other socket is already using the address.\n"); } else if (errno == EINVAL) { printf("The socket already has an address.\n"); } else if (errno == EACCES) { printf("Access to socket denied. (Maybe choose a port > 1024?)\n"); } else if (errno == EBADF) { printf("Bad file descriptor. (???)\n"); } else { printf("Unknown error (errno = %i)\n", errno); } exit(1); } printf("\n\t === Server socket started up and bound to port %i ===\n\n", args.port); printf("Listening for connections.\n"); if (listen(MySock, 10) < 0) { printf("%s: Listening error. Errno is %i.\n", __FILE__, errno); exit(1); } peer_size = sizeof(peer); if (chroot(args.basedir) < 0) { printf("chroot() failed!\n"); exit(1); } BufIn = malloc(1025); while ((NewSock = accept(MySock, (struct sockaddr *)&peer, &peer_size)) != -1) { char host_addr[16]; clear(BufIn, BufInLen); clear(BufOut, BufOutLen); strcpy(host_addr, inet_ntoa(peer.sin_addr)); hostentry = gethostbyname(host_addr); if (hostentry == NULL) { printf("Connection from %s, port %i.\n", host_addr, ntohs(peer.sin_port)); } else { printf("Connection from %s (%s), port %i.\n", hostentry->h_name, inet_ntoa(peer.sin_addr), ntohs(peer.sin_port)); } errno = 0; if (recv(NewSock, BufIn, 1024, 0) < 0) { printf("%s: recv() failed. ", __FILE__); if (errno == EBADF) { printf("Bad file descriptor. (???)\n"); } else if (errno == ENOTSOCK) { printf("Not a socket. (???)\n"); } else if (errno == EWOULDBLOCK) { printf("Would block nonblocking mode socket.\n"); } else if (errno == EINTR) { printf("Interrupted by a signal.\n"); } else if (errno == ENOTCONN) { printf("Socket disconnected.\n"); } else { printf("Unknown error (errno = %i)\n", errno); } errno = 0; } if (BufIn == NULL) { printf("Error: HTTP request was NULL!\n"); send(NewSock, "HTTP/1.0 505\n", 1024, 0); } else if (http_request(NewSock, BufIn) < 0) { printf("Socket error %i.\n", errno); errno = 0; } printf("Host disconnected.\n"); shutdown(NewSock, 2); close (NewSock); } if (NewSock == -1) { printf("Exit from bad accept() run (whatever might have happened).\n"); } printf("%s: Cleaning up. Closing socket: ", __FILE__); close (MySock); printf("OK.\n"); printf("Server: exit.\n"); exit(0); }