diff options
-rw-r--r-- | client.c | 38 | ||||
-rw-r--r-- | file.c | 211 | ||||
-rw-r--r-- | main.c | 7 | ||||
-rw-r--r-- | uhttpd.h | 27 |
4 files changed, 146 insertions, 137 deletions
@@ -27,12 +27,18 @@ static LIST_HEAD(clients); int n_clients = 0; struct config conf = {}; -static const char *http_versions[] = { +const char * const http_versions[] = { [UH_HTTP_VER_0_9] = "HTTP/0.9", [UH_HTTP_VER_1_0] = "HTTP/1.0", [UH_HTTP_VER_1_1] = "HTTP/1.1", }; +const char * const http_methods[] = { + [UH_HTTP_MSG_GET] = "GET", + [UH_HTTP_MSG_POST] = "POST", + [UH_HTTP_MSG_HEAD] = "HEAD", +}; + void uh_http_header(struct client *cl, int code, const char *summary) { const char *enc = "Transfer-Encoding: chunked\r\n"; @@ -113,11 +119,21 @@ static void client_timeout(struct uloop_timeout *timeout) uh_connection_close(cl); } +static int find_idx(const char * const *list, int max, const char *str) +{ + int i; + + for (i = 0; i < max; i++) + if (!strcmp(list[i], str)) + return i; + + return -1; +} + static int client_parse_request(struct client *cl, char *data) { struct http_request *req = &cl->request; char *type, *path, *version; - int i; type = strtok(data, " "); path = strtok(NULL, " "); @@ -126,23 +142,11 @@ static int client_parse_request(struct client *cl, char *data) return CLIENT_STATE_DONE; req->url = path; - if (!strcmp(type, "GET")) - req->method = UH_HTTP_MSG_GET; - else if (!strcmp(type, "POST")) - req->method = UH_HTTP_MSG_POST; - else if (!strcmp(type, "HEAD")) - req->method = UH_HTTP_MSG_HEAD; - else + req->method = find_idx(http_methods, ARRAY_SIZE(http_methods), type); + if (req->method < 0) return CLIENT_STATE_DONE; - cl->request.version = -1; - i = array_size(http_versions); - while (i--) { - if (!strcmp(version, http_versions[i])) { - cl->request.version = i; - break; - } - } + req->version = find_idx(http_versions, ARRAY_SIZE(http_versions), version); if (cl->request.version < 0) return CLIENT_STATE_DONE; @@ -30,6 +30,7 @@ #include "uhttpd.h" #include "mimetypes.h" +static char _tag[128]; static LIST_HEAD(index_files); struct index_file { @@ -37,16 +38,6 @@ struct index_file { const char *name; }; -struct path_info { - char *root; - char *phys; - char *name; - char *info; - char *query; - int redirected; - struct stat stat; -}; - enum file_hdr { HDR_IF_MODIFIED_SINCE, HDR_IF_UNMODIFIED_SINCE, @@ -67,21 +58,14 @@ void uh_index_add(const char *filename) static char * canonpath(const char *path, char *path_resolved) { - char path_copy[PATH_MAX]; - char *path_cpy = path_copy; + const char *path_cpy = path; char *path_res = path_resolved; - /* relative -> absolute */ - if (*path != '/') { - getcwd(path_copy, PATH_MAX); - strncat(path_copy, "/", PATH_MAX - strlen(path_copy)); - strncat(path_copy, path, PATH_MAX - strlen(path_copy)); - } else { - strncpy(path_copy, path, PATH_MAX); - } + if (conf.no_symlinks) + return realpath(path, path_resolved); /* normalize */ - while ((*path_cpy != '\0') && (path_cpy < (path_copy + PATH_MAX - 2))) { + while ((*path_cpy != '\0') && (path_cpy < (path + PATH_MAX - 2))) { if (*path_cpy != '/') goto next; @@ -134,13 +118,13 @@ uh_path_lookup(struct client *cl, const char *url) static char path_info[PATH_MAX]; static struct path_info p; - char buffer[UH_LIMIT_MSGHEAD]; - char *docroot = conf.docroot; + const char *docroot = conf.docroot; + int docroot_len = strlen(docroot); char *pathptr = NULL; + bool slash; - int slash = 0; - int no_sym = conf.no_symlinks; int i = 0; + int len; struct stat s; struct index_file *idx; @@ -148,14 +132,11 @@ uh_path_lookup(struct client *cl, const char *url) if (url == NULL) return NULL; - memset(path_phys, 0, sizeof(path_phys)); - memset(path_info, 0, sizeof(path_info)); - memset(buffer, 0, sizeof(buffer)); memset(&p, 0, sizeof(p)); + path_phys[0] = 0; + path_info[0] = 0; - /* copy docroot */ - memcpy(buffer, docroot, - min(strlen(docroot), sizeof(buffer) - 1)); + strcpy(uh_buf, docroot); /* separate query string from url */ if ((pathptr = strchr(url, '?')) != NULL) { @@ -163,94 +144,104 @@ uh_path_lookup(struct client *cl, const char *url) /* urldecode component w/o query */ if (pathptr > url) { - if (uh_urldecode(&buffer[strlen(docroot)], - sizeof(buffer) - strlen(docroot) - 1, - url, pathptr - url ) < 0) - return NULL; /* bad URL */ + if (uh_urldecode(&uh_buf[docroot_len], + sizeof(uh_buf) - docroot_len - 1, + url, pathptr - url ) < 0) + return NULL; } } /* no query string, decode all of url */ - else if (uh_urldecode(&buffer[strlen(docroot)], - sizeof(buffer) - strlen(docroot) - 1, + else if (uh_urldecode(&uh_buf[docroot_len], + sizeof(uh_buf) - docroot_len - 1, url, strlen(url) ) < 0) - return NULL; /* bad URL */ + return NULL; /* create canon path */ - for (i = strlen(buffer), slash = (buffer[max(0, i-1)] == '/'); i >= 0; i--) { - if ((buffer[i] == 0) || (buffer[i] == '/')) { - memset(path_info, 0, sizeof(path_info)); - memcpy(path_info, buffer, min(i + 1, sizeof(path_info) - 1)); - - if (no_sym ? realpath(path_info, path_phys) - : canonpath(path_info, path_phys)) { - memset(path_info, 0, sizeof(path_info)); - memcpy(path_info, &buffer[i], - min(strlen(buffer) - i, sizeof(path_info) - 1)); - - break; - } - } + len = strlen(uh_buf); + slash = len && uh_buf[len - 1] == '/'; + len = min(len, sizeof(path_phys) - 1); + + for (i = len; i >= 0; i--) { + char ch = uh_buf[i]; + bool exists; + + if (ch != 0 && ch != '/') + continue; + + uh_buf[i] = 0; + exists = !!canonpath(uh_buf, path_phys); + uh_buf[i] = ch; + + snprintf(path_info, sizeof(path_info), "%s", uh_buf + i); + break; } /* check whether found path is within docroot */ - if (strncmp(path_phys, docroot, strlen(docroot)) || - ((path_phys[strlen(docroot)] != 0) && - (path_phys[strlen(docroot)] != '/'))) + if (strncmp(path_phys, docroot, docroot_len) != 0 || + (path_phys[docroot_len] != 0 && + path_phys[docroot_len] != '/')) return NULL; /* test current path */ - if (!stat(path_phys, &p.stat)) { - /* is a regular file */ - if (p.stat.st_mode & S_IFREG) { - p.root = docroot; - p.phys = path_phys; - p.name = &path_phys[strlen(docroot)]; - p.info = path_info[0] ? path_info : NULL; - } + if (stat(path_phys, &p.stat)) + return NULL; - /* is a directory */ - else if ((p.stat.st_mode & S_IFDIR) && !strlen(path_info)) { - /* ensure trailing slash */ - if (path_phys[strlen(path_phys)-1] != '/') - path_phys[strlen(path_phys)] = '/'; - - /* try to locate index file */ - memset(buffer, 0, sizeof(buffer)); - memcpy(buffer, path_phys, sizeof(buffer)); - pathptr = &buffer[strlen(buffer)]; - - /* if requested url resolves to a directory and a trailing slash - is missing in the request url, redirect the client to the same - url with trailing slash appended */ - if (!slash) { - uh_http_header(cl, 302, "Found"); - ustream_printf(cl->us, "Location: %s%s%s\r\n\r\n", - &path_phys[strlen(docroot)], - p.query ? "?" : "", - p.query ? p.query : ""); - uh_request_done(cl); - p.redirected = 1; - } else { - list_for_each_entry(idx, &index_files, list) { - strncat(buffer, idx->name, sizeof(buffer)); - - if (!stat(buffer, &s) && (s.st_mode & S_IFREG)) { - memcpy(path_phys, buffer, sizeof(path_phys)); - memcpy(&p.stat, &s, sizeof(p.stat)); - break; - } - - *pathptr = 0; - } - } + /* is a regular file */ + if (p.stat.st_mode & S_IFREG) { + p.root = docroot; + p.phys = path_phys; + p.name = &path_phys[docroot_len]; + p.info = path_info[0] ? path_info : NULL; + return &p; + } - p.root = docroot; - p.phys = path_phys; - p.name = &path_phys[strlen(docroot)]; - } + if (!(p.stat.st_mode & S_IFDIR)) + return NULL; + + if (path_info[0]) + return NULL; + + pathptr = path_phys + strlen(path_phys); + + /* ensure trailing slash */ + if (pathptr[-1] != '/') { + pathptr[0] = '/'; + pathptr[1] = 0; + pathptr++; + } + + /* if requested url resolves to a directory and a trailing slash + is missing in the request url, redirect the client to the same + url with trailing slash appended */ + if (!slash) { + uh_http_header(cl, 302, "Found"); + ustream_printf(cl->us, "Location: %s%s%s\r\n\r\n", + &path_phys[docroot_len], + p.query ? "?" : "", + p.query ? p.query : ""); + uh_request_done(cl); + p.redirected = 1; + return &p; } + /* try to locate index file */ + len = path_phys + sizeof(path_phys) - pathptr - 1; + list_for_each_entry(idx, &index_files, list) { + if (strlen(idx->name) > len) + continue; + + strcpy(pathptr, idx->name); + if (!stat(path_phys, &s) && (s.st_mode & S_IFREG)) + break; + + *pathptr = 0; + } + + p.root = docroot; + p.phys = path_phys; + p.name = &path_phys[docroot_len]; + return p.phys ? &p : NULL; } @@ -281,14 +272,12 @@ static const char * uh_file_mime_lookup(const char *path) static const char * uh_file_mktag(struct stat *s) { - static char tag[128]; - - snprintf(tag, sizeof(tag), "\"%x-%x-%x\"", + snprintf(_tag, sizeof(_tag), "\"%x-%x-%x\"", (unsigned int) s->st_ino, (unsigned int) s->st_size, (unsigned int) s->st_mtime); - return tag; + return _tag; } static time_t uh_file_date2unix(const char *date) @@ -305,12 +294,11 @@ static time_t uh_file_date2unix(const char *date) static char * uh_file_unix2date(time_t ts) { - static char str[128]; struct tm *t = gmtime(&ts); - strftime(str, sizeof(str), "%a, %d %b %Y %H:%M:%S GMT", t); + strftime(_tag, sizeof(_tag), "%a, %d %b %Y %H:%M:%S GMT", t); - return str; + return _tag; } static char *uh_file_header(struct client *cl, int idx) @@ -528,12 +516,11 @@ static void uh_file_dirlist(struct client *cl, struct path_info *pi) static void file_write_cb(struct client *cl) { - char buf[512]; int fd = cl->dispatch.file.fd; int r; while (cl->us->w.data_bytes < 256) { - r = read(fd, buf, sizeof(buf)); + r = read(fd, uh_buf, sizeof(uh_buf)); if (r < 0) { if (errno == EINTR) continue; @@ -544,7 +531,7 @@ static void file_write_cb(struct client *cl) return; } - uh_chunk_write(cl, buf, r); + uh_chunk_write(cl, uh_buf, r); } } @@ -30,6 +30,7 @@ #include "uhttpd.h" +char uh_buf[4096]; static int run_server(void) { @@ -165,6 +166,7 @@ static void init_defaults(void) conf.max_requests = 3; conf.realm = "Protected Area"; conf.cgi_prefix = "/cgi-bin"; + conf.cgi_path = "/sbin:/usr/sbin:/bin:/usr/bin"; uh_index_add("index.html"); uh_index_add("index.htm"); @@ -180,6 +182,8 @@ int main(int argc, char **argv) int cur_fd; int bound = 0; + BUILD_BUG_ON(sizeof(uh_buf) < PATH_MAX); + init_defaults(); signal(SIGPIPE, SIG_IGN); @@ -195,11 +199,12 @@ int main(int argc, char **argv) break; case 'h': - if (!realpath(optarg, conf.docroot)) { + if (!realpath(optarg, uh_buf)) { fprintf(stderr, "Error: Invalid directory %s: %s\n", optarg, strerror(errno)); exit(1); } + conf.docroot = strdup(uh_buf); break; case 'E': @@ -34,14 +34,14 @@ #define UH_LIMIT_CLIENTS 64 #define UH_LIMIT_HEADERS 64 -#define UH_LIMIT_MSGHEAD 4096 struct config { - char docroot[PATH_MAX]; - char *realm; - char *file; - char *error_handler; - char *cgi_prefix; + const char *docroot; + const char *realm; + const char *file; + const char *error_handler; + const char *cgi_prefix; + const char *cgi_path; int no_symlinks; int no_dirlists; int network_timeout; @@ -52,6 +52,16 @@ struct config { int script_timeout; }; +struct path_info { + const char *root; + const char *phys; + const char *name; + const char *info; + const char *query; + int redirected; + struct stat stat; +}; + struct auth_realm { struct list_head list; char *path; @@ -76,7 +86,7 @@ struct http_request { enum http_version version; int redirect_status; char *url; - struct auth_realm *realm; + const struct auth_realm *realm; }; struct http_response { @@ -126,8 +136,11 @@ struct client { } dispatch; }; +extern char uh_buf[4096]; extern int n_clients; extern struct config conf; +extern const char * const http_versions[]; +extern const char * const http_methods[]; void uh_index_add(const char *filename); |