diff options
-rw-r--r-- | README | 6 | ||||
-rw-r--r-- | httpserve.c | 283 |
2 files changed, 240 insertions, 49 deletions
@@ -3,7 +3,7 @@ httpserv httpserv is a small server to serve up a directory over HTTP. It's usage is: - httpserve [-p port] [-d dir] + httpserve [-p port] [-d dir] [-a] By default it listens on port 80 and serves the current directory. To use port 80 on UNIX/Linux you'll have to run as root to have access to such a low @@ -14,3 +14,7 @@ the directory and links to download each one. For security reasons sub directories aren't supported. The basname of the passed file is used so that it must exist in the directory being served. + +By specifying the -a flag it will allow uploads via a POST. This is a basic +implmentation where it ignores the content-type and just dumps the body of the +request to the passed file. It will not allow files to be overwritten. diff --git a/httpserve.c b/httpserve.c index 946f4ae..98045ad 100644 --- a/httpserve.c +++ b/httpserve.c @@ -24,6 +24,7 @@ #include <string.h> #include <errno.h> #include <stdarg.h> +#include <ctype.h> #include <magic.h> @@ -45,9 +46,9 @@ typedef struct { int fd; magic_t magic; + int allow_post; } thread_arg_t; - /* --------------------------------------------------------------------------- BASE ROUTINES --------------------------------------------------------------------------- @@ -65,6 +66,42 @@ static char *Strdup(const char *p) return ret; } +static void Chomp(char *p) +{ + int len = strlen(p); + + len--; + + while(len >= 0 && (p[len] == '\r' || p[len] == '\n')) + { + p[len--] = 0; + } +} + +static const char *GetFilename(const char *path) +{ + const char *p = strrchr(path, '/'); + + if (p) + { + if (*(p + 1)) + { + return p + 1; + } + else + { + return p; + } + } + + return path; +} + +static int StrMatch(const char *a, const char *b) +{ + return strcasecmp(a,b) == 0; +} + static void *Malloc(size_t s) { void *ret = malloc(s); @@ -120,6 +157,36 @@ static char *PrintStream(char *stream, const char *format, ...) return stream; } +static int SplitHeader(char *buff, char **setting, char **value) +{ + char *p = buff; + + *setting = NULL; + *value = NULL; + + while(*p && !isspace((unsigned char)*p) && *p != ':') + { + p++; + } + + if (*p) + { + *setting = buff; + } + + while (*p && (isspace((unsigned char)*p) || *p == ':')) + { + *p++ = 0; + } + + if (*p) + { + *value = p; + } + + return *setting && *value; +} + /* --------------------------------------------------------------------------- SOCKET ROUTINES @@ -128,6 +195,7 @@ static char *PrintStream(char *stream, const char *format, ...) static int Listen(int port) { struct sockaddr_in addr; + int sockopt = 1; int fd; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) @@ -136,6 +204,13 @@ static int Listen(int port) return -1; } + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + &sockopt, sizeof sockopt) == -1) + { + perror("setsockopt"); + return -1; + } + addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); @@ -155,35 +230,59 @@ static int Listen(int port) return fd; } -static int Read(int fd, char *buff, size_t max) +static int Read(int fd, char *buff, size_t len) { int rd; size_t tot = 0; - while(tot < max) + while(tot < len) { - rd = read(fd, buff + tot, max - tot); + rd = read(fd, buff + tot, len - tot); if (rd < 1) { - return 0; + return tot > 0 ? tot : 0; } tot += rd; + } - if (tot < max) - { - buff[tot] = 0; + return tot; +} - if (tot > 4 && buff[tot - 4] == '\r' && buff[tot - 3] == '\n' && - buff[tot - 2] == '\r' && buff[tot - 1] == '\n') - { - return 1; - } +static int ReadByte(int fd, unsigned char *byte) +{ + if (read(fd, byte, 1) == 1) + { + return 1; + } + else + { + return 0; + } +} + +static int ReadLine(int fd, char *line, size_t max_size) +{ + unsigned char c; + unsigned char last = 0; + size_t len = 0; + + while(ReadByte(fd, &c) && len < max_size - 1) + { + line[len] = c; + line[len + 1] = 0; + + if (c == '\n' && last == '\r') + { + Chomp(line); + return 1; } + + len++; + last = c; } - fprintf(stderr, "HTTP request too big\n"); return 0; } @@ -297,12 +396,14 @@ static void DecodeURL(char *dest, const char *url) *dest = 0; } -static int ParseRequest(char *request, - char **method, - char **path, - char **protocol, - int *close) +static int GetRequest(int fd, + char **method, + char **path, + char **protocol, + size_t *content_length, + int *close) { + char buff[0x4000]; char *tok[3] = {0}; char *last; int f; @@ -310,34 +411,69 @@ static int ParseRequest(char *request, *method = NULL; *path = NULL; *protocol = NULL; + *content_length = 0; *close = 0; - if ((tok[0] = strtok_r(request, " ", &last)) && - (tok[1] = strtok_r(NULL, " ", &last)) && - (tok[2] = strtok_r(NULL, " \r\n", &last))) + if (ReadLine(fd, buff, sizeof buff)) { - char *tmppath; + if ((tok[0] = strtok_r(buff, " ", &last)) && + (tok[1] = strtok_r(NULL, " ", &last)) && + (tok[2] = strtok_r(NULL, " \r\n", &last))) + { + char *tmppath; - *method = tok[0]; - *path = tok[1]; - *protocol = tok[2]; + *method = Strdup(tok[0]); + *path = Strdup(tok[1]); + *protocol = Strdup(tok[2]); - /* This is slightly dodgy as hell, but should work - */ - *close = (strstr(*protocol + strlen(*protocol) + 3, - "Connection: close\r\n") || - strcmp(*protocol, "HTTP/1.0") == 0); + tmppath = Strdup(*path); + DecodeURL(*path, tmppath); + free(tmppath); - tmppath = Strdup(*path); - DecodeURL(*path, tmppath); - free(tmppath); + if(strcmp(*protocol, "HTTP/1.0") == 0) + { + *close = 1; + } + } + + while(ReadLine(fd, buff, sizeof buff) && buff[0]) + { + if (SplitHeader(buff, &tok[0], &tok[1])) + { + if (StrMatch(tok[0], "connection") && StrMatch(tok[1], "close")) + { + *close = 1; + } + else if (StrMatch(tok[0], "content-length")) + { + *content_length = (size_t)atoll(tok[1]); + } + } + } return 1; } + *close = 1; return 0; } +static char *ReadContents(int fd, size_t len) +{ + char *contents = Malloc(len); + + if (contents) + { + if (Read(fd, contents, len) != len) + { + free(contents); + contents = NULL; + } + } + + return contents; +} + /* --------------------------------------------------------------------------- THREAD CODE @@ -345,33 +481,35 @@ static int ParseRequest(char *request, */ static void *ThreadCode(void *p) { - char buff[4096]; char *method = NULL; char *path = NULL; char *protocol = NULL; + size_t content_len = 0; int close_connection = 0; thread_arg_t *args = p; - while (Read(args->fd, buff, sizeof buff) && !close_connection) + while (!close_connection) { - if (ParseRequest(buff, &method, &path, - &protocol, &close_connection)) + if (GetRequest(args->fd, &method, &path, + &protocol, &content_len, &close_connection)) { - if (strcmp(method, "GET") == 0) - { - printf("%s %s %s\n", method, path, protocol); + const char *filename; + + printf("%s %s %s\n", method, path, protocol); - path = basename(path); + filename = GetFilename(path); - if (strcmp(path, "/") == 0) + if (strcmp(method, "GET") == 0) + { + if (strcmp(filename, "/") == 0) { printf("Generate listing for /\n"); GenerateListing(args->fd, protocol); } - else if (access(path, R_OK) == 0) + else if (access(filename, R_OK) == 0) { - printf("Generating download for %s\n", path); - GenerateFile(args->fd, args->magic, path, protocol); + printf("Generating download for %s\n", filename); + GenerateFile(args->fd, args->magic, filename, protocol); } else { @@ -379,15 +517,58 @@ static void *ThreadCode(void *p) GenerateStatus(args->fd, 404, "Not found", protocol); } } + else if (strcmp(method, "POST") == 0 && args->allow_post) + { + char *contents; + + if ((contents = ReadContents(args->fd, content_len))) + { + int file = -1; + + printf("Got body of %lu bytes\n", + (unsigned long)content_len); + + if (!StrMatch(filename, "/") && + (file = open(filename, + O_WRONLY|O_CREAT|O_EXCL)) != -1 && + write(file, contents, content_len) == + content_len) + { + GenerateStatus(args->fd, 200, "OK", protocol); + } + else + { + GenerateStatus(args->fd, 500, + "Failed to write contents", protocol); + } + + if (file != -1) + { + close(file); + } + + free(contents); + } + else + { + GenerateStatus(args->fd, 500, + "Failed to read contents from request", + protocol); + } + } else { printf("Method %s not supported\n", method); GenerateStatus(args->fd, 501, "Not implemented", protocol); } + + free(method); + free(path); + free(protocol); } else { - printf("Bad request %s\n", method); + printf("Bad request\n"); } } @@ -409,8 +590,9 @@ int main(int argc, char *argv[]) int opt; int sock; const char *dir="."; + int allow_post = 0; - while((opt = getopt(argc, argv, "p:d:")) != -1) + while((opt = getopt(argc, argv, "p:d:a")) != -1) { switch(opt) { @@ -422,6 +604,10 @@ int main(int argc, char *argv[]) dir = optarg; break; + case 'a': + allow_post = 1; + break; + default: fprintf(stderr, "%s: usage %s [-p port] [-d dir]\n", argv[0], argv[0]); @@ -465,6 +651,7 @@ int main(int argc, char *argv[]) thread_args = Malloc(sizeof *thread_args); thread_args->fd = fd; thread_args->magic = magic; + thread_args->allow_post = allow_post; pthread_create(&thread, NULL, ThreadCode, thread_args); pthread_detach(thread); |