int http_response_redirect_to_directory(server *srv, connection *con) { buffer *o; o = buffer_init(); if (con->conf.is_ssl) { buffer_copy_string_len(o, CONST_STR_LEN("https://")); } else { buffer_copy_string_len(o, CONST_STR_LEN("http://")); } if (con->uri.authority->used) { buffer_append_string_buffer(o, con->uri.authority); } else { /* get the name of the currently connected socket */ struct hostent *he; #ifdef HAVE_IPV6 char hbuf[256]; #endif sock_addr our_addr; socklen_t our_addr_len; our_addr_len = sizeof(our_addr); if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) { con->http_status = 500; log_error_write(srv, __FILE__, __LINE__, "ss", "can't get sockname", strerror(errno)); buffer_free(o); return 0; } /* Lookup name: secondly try to get hostname for bind address */ switch(our_addr.plain.sa_family) { #ifdef HAVE_IPV6 case AF_INET6: if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6), SA_LEN((const struct sockaddr *)&our_addr.ipv6), hbuf, sizeof(hbuf), NULL, 0, 0)) { char dst[INET6_ADDRSTRLEN]; log_error_write(srv, __FILE__, __LINE__, "SSS", "NOTICE: getnameinfo failed: ", strerror(errno), ", using ip-address instead"); buffer_append_string(o, inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr, dst, sizeof(dst))); } else { buffer_append_string(o, hbuf); } break; #endif case AF_INET: if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) { log_error_write(srv, __FILE__, __LINE__, "SdS", "NOTICE: gethostbyaddr failed: ", h_errno, ", using ip-address instead"); buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr)); } else { buffer_append_string(o, he->h_name); } break; default: log_error_write(srv, __FILE__, __LINE__, "S", "ERROR: unsupported address-type"); buffer_free(o); return -1; } if (!((con->conf.is_ssl == 0 && srv->srvconf.port == 80) || (con->conf.is_ssl == 1 && srv->srvconf.port == 443))) { buffer_append_string_len(o, CONST_STR_LEN(":")); buffer_append_long(o, srv->srvconf.port); } } buffer_append_string_buffer(o, con->uri.path); buffer_append_string_len(o, CONST_STR_LEN("/")); if (!buffer_is_empty(con->uri.query)) { buffer_append_string_len(o, CONST_STR_LEN("?")); buffer_append_string_buffer(o, con->uri.query); } response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o)); con->http_status = 301; con->file_finished = 1; buffer_free(o); return 0; }
int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { lua_State *L; readme rm; int ret = -1; buffer *b = buffer_init(); int header_tbl = 0; rm.done = 0; stream_open(&rm.st, fn); /* push the lua file to the interpreter and see what happends */ L = luaL_newstate(); luaL_openlibs(L); /* register functions */ lua_register(L, "md5", f_crypto_md5); lua_register(L, "file_mtime", f_file_mtime); lua_register(L, "file_isreg", f_file_isreg); lua_register(L, "file_isdir", f_file_isreg); lua_register(L, "dir_files", f_dir_files); #ifdef HAVE_MEMCACHE_H lua_pushliteral(L, "memcache_get_long"); lua_pushlightuserdata(L, p->conf.mc); lua_pushcclosure(L, f_memcache_get_long, 1); lua_settable(L, LUA_GLOBALSINDEX); lua_pushliteral(L, "memcache_get_string"); lua_pushlightuserdata(L, p->conf.mc); lua_pushcclosure(L, f_memcache_get_string, 1); lua_settable(L, LUA_GLOBALSINDEX); lua_pushliteral(L, "memcache_exists"); lua_pushlightuserdata(L, p->conf.mc); lua_pushcclosure(L, f_memcache_exists, 1); lua_settable(L, LUA_GLOBALSINDEX); #endif /* register CGI environment */ lua_pushliteral(L, "request"); lua_newtable(L); lua_settable(L, LUA_GLOBALSINDEX); lua_pushliteral(L, "request"); header_tbl = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); if (!buffer_is_empty(con->request.pathinfo)) { c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); } c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl)); /* register GET parameter */ lua_pushliteral(L, "get"); lua_newtable(L); lua_settable(L, LUA_GLOBALSINDEX); lua_pushliteral(L, "get"); header_tbl = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); buffer_copy_string_buffer(b, con->uri.query); cache_export_get_params(L, header_tbl, b); buffer_reset(b); /* 2 default constants */ lua_pushliteral(L, "CACHE_HIT"); lua_pushnumber(L, 0); lua_settable(L, LUA_GLOBALSINDEX); lua_pushliteral(L, "CACHE_MISS"); lua_pushnumber(L, 1); lua_settable(L, LUA_GLOBALSINDEX); /* load lua program */ if (lua_load(L, load_file, &rm, fn->ptr) || lua_pcall(L,0,1,0)) { log_error_write(srv, __FILE__, __LINE__, "s", lua_tostring(L,-1)); goto error; } /* get return value */ ret = (int)lua_tonumber(L, -1); lua_pop(L, 1); /* fetch the data from lua */ lua_to_c_get_string(L, "trigger_handler", p->trigger_handler); if (0 == lua_to_c_get_string(L, "output_contenttype", b)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b)); } if (ret == 0) { /* up to now it is a cache-hit, check if all files exist */ int curelem; time_t mtime = 0; if (!lua_to_c_is_table(L, "output_include")) { log_error_write(srv, __FILE__, __LINE__, "s", "output_include is missing or not a table"); ret = -1; goto error; } lua_pushstring(L, "output_include"); curelem = lua_gettop(L); lua_gettable(L, LUA_GLOBALSINDEX); /* HOW-TO build a etag ? * as we don't just have one file we have to take the stat() * from all base files, merge them and build the etag from * it later. * * The mtime of the content is the mtime of the freshest base file * * */ lua_pushnil(L); /* first key */ while (lua_next(L, curelem) != 0) { stat_cache_entry *sce = NULL; /* key' is at index -2 and value' at index -1 */ if (lua_isstring(L, -1)) { const char *s = lua_tostring(L, -1); /* the file is relative, make it absolute */ if (s[0] != '/') { buffer_copy_string_buffer(b, p->basedir); buffer_append_string(b, lua_tostring(L, -1)); } else { buffer_copy_string(b, lua_tostring(L, -1)); } if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) { /* stat failed */ switch(errno) { case ENOENT: /* a file is missing, call the handler to generate it */ if (!buffer_is_empty(p->trigger_handler)) { ret = 1; /* cache-miss */ log_error_write(srv, __FILE__, __LINE__, "s", "a file is missing, calling handler"); break; } else { /* handler not set -> 500 */ ret = -1; log_error_write(srv, __FILE__, __LINE__, "s", "a file missing and no handler set"); break; } break; default: break; } } else { chunkqueue_append_file(con->write_queue, b, 0, sce->st.st_size); if (sce->st.st_mtime > mtime) mtime = sce->st.st_mtime; } } else { /* not a string */ ret = -1; log_error_write(srv, __FILE__, __LINE__, "s", "not a string"); break; } lua_pop(L, 1); /* removes value'; keeps key' for next iteration */ } lua_settop(L, curelem - 1); if (ret == 0) { data_string *ds; char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; buffer tbuf; con->file_finished = 1; ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"); /* no Last-Modified specified */ if ((mtime) && (NULL == ds)) { strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime)); response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1); tbuf.ptr = timebuf; tbuf.used = sizeof(timebuf); tbuf.size = sizeof(timebuf); } else if (ds) { tbuf.ptr = ds->value->ptr; tbuf.used = ds->value->used; tbuf.size = ds->value->size; } else { tbuf.size = 0; tbuf.used = 0; tbuf.ptr = NULL; } if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf, con->physical.etag)) { /* ok, the client already has our content, * no need to send it again */ chunkqueue_reset(con->write_queue); ret = 0; /* cache-hit */ } } else { chunkqueue_reset(con->write_queue); } } if (ret == 1 && !buffer_is_empty(p->trigger_handler)) { /* cache-miss */ buffer_copy_string_buffer(con->uri.path, p->baseurl); buffer_append_string_buffer(con->uri.path, p->trigger_handler); buffer_copy_string_buffer(con->physical.path, p->basedir); buffer_append_string_buffer(con->physical.path, p->trigger_handler); chunkqueue_reset(con->write_queue); } error: lua_close(L); stream_close(&rm.st); buffer_free(b); return ret /* cache-error */; }
fdevents *fdevent_init(server *srv, size_t maxfds, fdevent_handler_t type) { fdevents *ev; ev = calloc(1, sizeof(*ev)); ev->srv = srv; ev->fdarray = calloc(maxfds, sizeof(*ev->fdarray)); ev->maxfds = maxfds; switch(type) { case FDEVENT_HANDLER_POLL: if (0 != fdevent_poll_init(ev)) { log_error_write(ev->srv, __FILE__, __LINE__, "S", "event-handler poll failed"); return NULL; } return ev; case FDEVENT_HANDLER_SELECT: if (0 != fdevent_select_init(ev)) { log_error_write(ev->srv, __FILE__, __LINE__, "S", "event-handler select failed"); return NULL; } return ev; case FDEVENT_HANDLER_LINUX_SYSEPOLL: if (0 != fdevent_linux_sysepoll_init(ev)) { log_error_write(ev->srv, __FILE__, __LINE__, "S", "event-handler linux-sysepoll failed, try to set server.event-handler = \"poll\" or \"select\""); return NULL; } return ev; case FDEVENT_HANDLER_SOLARIS_DEVPOLL: if (0 != fdevent_solaris_devpoll_init(ev)) { log_error_write(ev->srv, __FILE__, __LINE__, "S", "event-handler solaris-devpoll failed, try to set server.event-handler = \"poll\" or \"select\""); return NULL; } return ev; case FDEVENT_HANDLER_SOLARIS_PORT: if (0 != fdevent_solaris_port_init(ev)) { log_error_write(ev->srv, __FILE__, __LINE__, "S", "event-handler solaris-eventports failed, try to set server.event-handler = \"poll\" or \"select\""); return NULL; } return ev; case FDEVENT_HANDLER_FREEBSD_KQUEUE: if (0 != fdevent_freebsd_kqueue_init(ev)) { log_error_write(ev->srv, __FILE__, __LINE__, "S", "event-handler freebsd-kqueue failed, try to set server.event-handler = \"poll\" or \"select\""); return NULL; } return ev; case FDEVENT_HANDLER_LIBEV: if (0 != fdevent_libev_init(ev)) { log_error_write(ev->srv, __FILE__, __LINE__, "S", "event-handler libev failed, try to set server.event-handler = \"poll\" or \"select\""); return NULL; } return ev; case FDEVENT_HANDLER_UNSET: break; } log_error_write(ev->srv, __FILE__, __LINE__, "S", "event-handler is unknown, try to set server.event-handler = \"poll\" or \"select\""); return NULL; }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { char *args[3]; int to_cgi_fds[2]; int from_cgi_fds[2]; int dfd = -1; UNUSED(p); if (!buffer_string_is_empty(cgi_handler)) { if (NULL == cgi_stat(srv, con, cgi_handler)) { log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, "failed:", strerror(errno)); return -1; } } if (pipe_cloexec(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe_cloexec(from_cgi_fds)) { close(to_cgi_fds[0]); close(to_cgi_fds[1]); log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } fdevent_setfd_cloexec(to_cgi_fds[1]); fdevent_setfd_cloexec(from_cgi_fds[0]); { size_t i = 0; http_cgi_opts opts = { 0, 0, NULL, NULL }; env_accum *env = &p->env; env->used = 0; env->oused = 0; /* create environment */ http_cgi_headers(srv, con, &opts, cgi_env_add, env); /* for valgrind */ if (p->env.ld_preload) { cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload)); } if (p->env.ld_library_path) { cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path)); } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ if (p->env.systemroot) { cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot)); } #endif if (env->esize <= env->oused) { env->esize = (env->oused + 1 + 0xf) & ~(0xfuL); env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr)); force_assert(env->eptr); } for (i = 0; i < env->oused; ++i) { env->eptr[i] = env->ptr + env->offsets[i]; } env->eptr[env->oused] = NULL; /* set up args */ i = 0; if (!buffer_string_is_empty(cgi_handler)) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; args[i ] = NULL; } dfd = fdevent_open_dirname(con->physical.path->ptr, con->conf.follow_symlink); if (-1 == dfd) { log_error_write(srv, __FILE__, __LINE__, "ssb", "open dirname failed:", strerror(errno), con->physical.path); } hctx->pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, p->env.eptr, to_cgi_fds[0], from_cgi_fds[1], -1, dfd) : -1; if (-1 == hctx->pid) { /* log error with errno prior to calling close() (might change errno) */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); if (-1 != dfd) close(dfd); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(to_cgi_fds[0]); close(to_cgi_fds[1]); return -1; } else { if (-1 != dfd) close(dfd); close(from_cgi_fds[1]); close(to_cgi_fds[0]); hctx->fd = from_cgi_fds[0]; ++srv->cur_fds; cgi_pid_add(p, hctx->pid, hctx); if (0 == con->request.content_length) { close(to_cgi_fds[1]); } else { /* there is content to send */ if (-1 == fdevent_fcntl_set_nb(srv->ev, to_cgi_fds[1])) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) { close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } ++srv->cur_fds; } hctx->fdn = fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); if (-1 == fdevent_fcntl_set_nb(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); cgi_connection_close(srv, hctx); return -1; } fdevent_fdnode_event_set(srv->ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP); return 0; } }
static int http_list_directory(server *srv, connection *con, plugin_data *p, buffer *dir) { DIR *dp; buffer *out; struct dirent *dent; struct stat st; char *path, *path_file; size_t i; int hide_dotfiles = p->conf.hide_dot_files; dirls_list_t dirs, files, *list; dirls_entry_t *tmp; char sizebuf[sizeof("999.9K")]; char datebuf[sizeof("2005-Jan-01 22:23:24")]; size_t k; const char *content_type; long name_max; #ifdef HAVE_XATTR char attrval[128]; int attrlen; #endif #ifdef HAVE_LOCALTIME_R struct tm tm; #endif if (dir->used == 0) return -1; i = dir->used - 1; #ifdef HAVE_PATHCONF if (-1 == (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { #ifdef NAME_MAX name_max = NAME_MAX; #else name_max = 255; /* stupid default */ #endif } #elif defined __WIN32 name_max = FILENAME_MAX; #else name_max = NAME_MAX; #endif path = malloc(dir->used + name_max); assert(path); strcpy(path, dir->ptr); path_file = path + i; if (NULL == (dp = opendir(path))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opendir failed:", dir, strerror(errno)); free(path); return -1; } dirs.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); assert(dirs.ent); dirs.size = DIRLIST_BLOB_SIZE; dirs.used = 0; files.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); assert(files.ent); files.size = DIRLIST_BLOB_SIZE; files.used = 0; while ((dent = readdir(dp)) != NULL) { unsigned short exclude_match = 0; if (dent->d_name[0] == '.') { if (hide_dotfiles) continue; if (dent->d_name[1] == '\0') continue; if (dent->d_name[1] == '.' && dent->d_name[2] == '\0') continue; } if (p->conf.hide_readme_file) { if (strcmp(dent->d_name, "README.txt") == 0) continue; } if (p->conf.hide_header_file) { if (strcmp(dent->d_name, "HEADER.txt") == 0) continue; } /* compare d_name against excludes array * elements, skipping any that match. */ #ifdef HAVE_PCRE_H for(i = 0; i < p->conf.excludes->used; i++) { int n; #define N 10 int ovec[N * 3]; pcre *regex = p->conf.excludes->ptr[i]->regex; if ((n = pcre_exec(regex, NULL, dent->d_name, strlen(dent->d_name), 0, 0, ovec, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { log_error_write(srv, __FILE__, __LINE__, "sd", "execution error while matching:", n); return -1; } } else { exclude_match = 1; break; } } if (exclude_match) { continue; } #endif i = strlen(dent->d_name); /* NOTE: the manual says, d_name is never more than NAME_MAX * so this should actually not be a buffer-overflow-risk */ if (i > (size_t)name_max) continue; memcpy(path_file, dent->d_name, i + 1); if (stat(path, &st) != 0) continue; list = &files; if (S_ISDIR(st.st_mode)) list = &dirs; if (list->used == list->size) { list->size += DIRLIST_BLOB_SIZE; list->ent = (dirls_entry_t**) realloc(list->ent, sizeof(dirls_entry_t*) * list->size); assert(list->ent); } tmp = (dirls_entry_t*) malloc(sizeof(dirls_entry_t) + 1 + i); tmp->mtime = st.st_mtime; tmp->size = st.st_size; tmp->namelen = i; memcpy(DIRLIST_ENT_NAME(tmp), dent->d_name, i + 1); list->ent[list->used++] = tmp; } closedir(dp); if (dirs.used) http_dirls_sort(dirs.ent, dirs.used); if (files.used) http_dirls_sort(files.ent, files.used); out = chunkqueue_get_append_buffer(con->write_queue); buffer_copy_string_len(out, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"")); if (buffer_is_empty(p->conf.encoding)) { buffer_append_string_len(out, CONST_STR_LEN("iso-8859-1")); } else { buffer_append_string_buffer(out, p->conf.encoding); } buffer_append_string_len(out, CONST_STR_LEN("\"?>\n")); http_list_directory_header(srv, con, p, out); /* directories */ for (i = 0; i < dirs.used; i++) { tmp = dirs.ent[i]; #ifdef HAVE_LOCALTIME_R localtime_r(&(tmp->mtime), &tm); strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); #else strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); #endif buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\"")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); buffer_append_string_len(out, CONST_STR_LEN("/\">")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">")); buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- </td><td class=\"t\">Directory</td></tr>\n")); free(tmp); } /* files */ for (i = 0; i < files.used; i++) { tmp = files.ent[i]; content_type = NULL; #ifdef HAVE_XATTR if (con->conf.use_xattr) { memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); attrlen = sizeof(attrval) - 1; if (attr_get(path, "Content-Type", attrval, &attrlen, 0) == 0) { attrval[attrlen] = '\0'; content_type = attrval; } } #endif if (content_type == NULL) { content_type = "application/octet-stream"; for (k = 0; k < con->conf.mimetypes->used; k++) { data_string *ds = (data_string *)con->conf.mimetypes->data[k]; size_t ct_len; if (ds->key->used == 0) continue; ct_len = ds->key->used - 1; if (tmp->namelen < ct_len) continue; if (0 == strncasecmp(DIRLIST_ENT_NAME(tmp) + tmp->namelen - ct_len, ds->key->ptr, ct_len)) { content_type = ds->value->ptr; break; } } } #ifdef HAVE_LOCALTIME_R localtime_r(&(tmp->mtime), &tm); strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); #else strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); #endif http_list_directory_sizefmt(sizebuf, tmp->size); buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\"")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); buffer_append_string_len(out, CONST_STR_LEN("\">")); buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">")); buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">")); buffer_append_string(out, sizebuf); buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">")); buffer_append_string(out, content_type); buffer_append_string_len(out, CONST_STR_LEN("</td></tr>\n")); free(tmp); } free(files.ent); free(dirs.ent); free(path); http_list_directory_footer(srv, con, p, out); /* Insert possible charset to Content-Type */ if (buffer_is_empty(p->conf.encoding)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } else { buffer_copy_string_len(p->content_charset, CONST_STR_LEN("text/html; charset=")); buffer_append_string_buffer(p->content_charset, p->conf.encoding); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset)); } con->file_finished = 1; return 0; }
static int cgi_write_request(server *srv, handler_ctx *hctx, int fd) { connection *con = hctx->remote_conn; chunkqueue *cq = con->request_content_queue; chunk *c; /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. * solution: if this is still a problem on windows, then substitute * socketpair() for pipe() and closesocket() for close() on windows. */ for (c = cq->first; c; c = cq->first) { ssize_t r = -1; switch(c->type) { case FILE_CHUNK: r = cgi_write_file_chunk_mmap(srv, con, fd, cq); break; case MEM_CHUNK: if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) { switch(errno) { case EAGAIN: case EINTR: /* ignore and try again */ r = 0; break; case EPIPE: case ECONNRESET: /* connection closed */ r = -2; break; default: /* fatal error */ log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno)); r = -1; break; } } else if (r > 0) { chunkqueue_mark_written(cq, r); } break; } if (0 == r) break; /*(might block)*/ switch (r) { case -1: /* fatal error */ return -1; case -2: /* connection reset */ log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI"); /* skip all remaining data */ chunkqueue_mark_written(cq, chunkqueue_length(cq)); break; default: break; } } if (cq->bytes_out == (off_t)con->request.content_length && !hctx->conf.upgrade) { /* sent all request body input */ /* close connection to the cgi-script */ if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ --srv->cur_fds; if (close(fd)) { log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", fd, strerror(errno)); } } else { cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/ } } else { off_t cqlen = cq->bytes_in - cq->bytes_out; if (cq->bytes_in != con->request.content_length && cqlen < 65536 - 16384) { /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; con->is_readable = 1; /* trigger optimistic read from client */ } } if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ hctx->fdtocgi = fd; hctx->fdntocgi = fdevent_register(srv->ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); } if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ if ((fdevent_fdnode_interest(hctx->fdntocgi) & FDEVENT_OUT)) { fdevent_fdnode_event_set(srv->ev, hctx->fdntocgi, 0); } } else { /* more request body remains to be sent to CGI so register for fdevents */ fdevent_fdnode_event_set(srv->ev, hctx->fdntocgi, FDEVENT_OUT); } } return 0; }
/* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes), * also mmaps and sends complete chunk instead of only small parts - the files * are supposed to be temp files with reasonable chunk sizes. * * Also always use mmap; the files are "trusted", as we created them. */ static ssize_t cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) { chunk* const c = cq->first; off_t offset, toSend, file_end; ssize_t r; size_t mmap_offset, mmap_avail; char *data = NULL; force_assert(NULL != c); force_assert(FILE_CHUNK == c->type); force_assert(c->offset >= 0 && c->offset <= c->file.length); offset = c->file.start + c->offset; toSend = c->file.length - c->offset; file_end = c->file.start + c->file.length; /* offset to file end in this chunk */ if (0 == toSend) { chunkqueue_remove_finished_chunks(cq); return 0; } /*(simplified from chunk.c:chunkqueue_open_file_chunk())*/ UNUSED(con); if (-1 == c->file.fd) { if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, con->conf.follow_symlink, O_RDONLY, 0))) { log_error_write(srv, __FILE__, __LINE__, "ssb", "open failed:", strerror(errno), c->mem); return -1; } } /* (re)mmap the buffer if range is not covered completely */ if (MAP_FAILED == c->file.mmap.start || offset < c->file.mmap.offset || file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) { if (MAP_FAILED != c->file.mmap.start) { munmap(c->file.mmap.start, c->file.mmap.length); c->file.mmap.start = MAP_FAILED; } c->file.mmap.offset = mmap_align_offset(offset); c->file.mmap.length = file_end - c->file.mmap.offset; if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) { if (toSend > 65536) toSend = 65536; data = malloc(toSend); force_assert(data); if (-1 == lseek(c->file.fd, offset, SEEK_SET) || 0 >= (toSend = read(c->file.fd, data, toSend))) { if (-1 == toSend) { log_error_write(srv, __FILE__, __LINE__, "ssbdo", "lseek/read failed:", strerror(errno), c->mem, c->file.fd, offset); } else { /*(0 == toSend)*/ log_error_write(srv, __FILE__, __LINE__, "sbdo", "unexpected EOF (input truncated?):", c->mem, c->file.fd, offset); } free(data); return -1; } } } if (MAP_FAILED != c->file.mmap.start) { force_assert(offset >= c->file.mmap.offset); mmap_offset = offset - c->file.mmap.offset; force_assert(c->file.mmap.length > mmap_offset); mmap_avail = c->file.mmap.length - mmap_offset; force_assert(toSend <= (off_t) mmap_avail); data = c->file.mmap.start + mmap_offset; } r = write(fd, data, toSend); if (MAP_FAILED == c->file.mmap.start) free(data); if (r < 0) { switch (errno) { case EAGAIN: case EINTR: return 0; case EPIPE: case ECONNRESET: return -2; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), fd); return -1; } } chunkqueue_mark_written(cq, r); return r; }
int http_response_write_header(server *srv, connection *con) { buffer *b; size_t i; int have_date = 0; int have_server = 0; b = buffer_init(); if (con->request.http_version == HTTP_VERSION_1_1) { buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 ")); } else { buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 ")); } buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" ")); buffer_append_string(b, get_http_status_name(con->http_status)); /* disable keep-alive if requested */ if (con->request_count > con->conf.max_keep_alive_requests || 0 == con->conf.max_keep_alive_idle) { con->keep_alive = 0; } else { con->keep_alive_idle = con->conf.max_keep_alive_idle; } if (con->request.http_version != HTTP_VERSION_1_1 || con->keep_alive == 0) { if (con->keep_alive) { response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); } else { response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); } } if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { response_header_overwrite(srv, con, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked")); } /* add all headers */ for (i = 0; i < con->response.headers->used; i++) { data_string *ds; ds = (data_string *)con->response.headers->data[i]; if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key) && 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-")) && 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-Sendfile"))) { if (0 == strcasecmp(ds->key->ptr, "Date")) have_date = 1; if (0 == strcasecmp(ds->key->ptr, "Server")) have_server = 1; if (0 == strcasecmp(ds->key->ptr, "Content-Encoding") && 304 == con->http_status) continue; buffer_append_string_len(b, CONST_STR_LEN("\r\n")); buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN(": ")); #if 0 /** * the value might contain newlines, encode them with at least one white-space */ buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_HTTP_HEADER); #else buffer_append_string_buffer(b, ds->value); #endif } } if (!have_date) { /* HTTP/1.1 requires a Date: header */ buffer_append_string_len(b, CONST_STR_LEN("\r\nDate: ")); /* cache the generated timestamp */ if (srv->cur_ts != srv->last_generated_date_ts) { buffer_string_prepare_copy(srv->ts_date_str, 255); buffer_append_strftime(srv->ts_date_str, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts))); srv->last_generated_date_ts = srv->cur_ts; } buffer_append_string_buffer(b, srv->ts_date_str); } if (!have_server) { if (buffer_is_empty(con->conf.server_tag)) { buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: " PACKAGE_DESC)); } else if (!buffer_string_is_empty(con->conf.server_tag)) { buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: ")); buffer_append_string_encoded(b, CONST_BUF_LEN(con->conf.server_tag), ENCODING_HTTP_HEADER); } } buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); con->bytes_header = buffer_string_length(b); if (con->conf.log_response_header) { log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b); } chunkqueue_prepend_buffer(con->write_queue, b); buffer_free(b); return 0; }
handler_t http_response_prepare(server *srv, connection *con) { handler_t r; /* looks like someone has already done a decision */ if (con->mode == DIRECT && (con->http_status != 0 && con->http_status != 200)) { /* remove a packets in the queue */ if (con->file_finished == 0) { chunkqueue_reset(con->write_queue); } return HANDLER_FINISHED; } /* no decision yet, build conf->filename */ if (con->mode == DIRECT && buffer_is_empty(con->physical.path)) { char *qstr; /* we only come here when we have the parse the full request again * * a HANDLER_COMEBACK from mod_rewrite and mod_fastcgi might be a * problem here as mod_setenv might get called multiple times * * fastcgi-auth might lead to a COMEBACK too * fastcgi again dead server too * * mod_compress might add headers twice too * * */ config_cond_cache_reset(srv, con); config_setup_connection(srv, con); /* Perhaps this could be removed at other places. */ if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "run condition"); } /** * prepare strings * * - uri.path_raw * - uri.path (secure) * - uri.query * */ /** * Name according to RFC 2396 * * - scheme * - authority * - path * - query * * (scheme)://(authority)(path)?(query)#fragment * * */ /* initial scheme value. can be overwritten for example by mod_extforward later */ if (con->srv_socket->is_ssl) { buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("https")); } else { buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("http")); } buffer_copy_buffer(con->uri.authority, con->request.http_host); buffer_to_lower(con->uri.authority); /** their might be a fragment which has to be cut away */ if (NULL != (qstr = strchr(con->request.uri->ptr, '#'))) { buffer_string_set_length(con->request.uri, qstr - con->request.uri->ptr); } /** extract query string from request.uri */ if (NULL != (qstr = strchr(con->request.uri->ptr, '?'))) { buffer_copy_string (con->uri.query, qstr + 1); buffer_copy_string_len(con->uri.path_raw, con->request.uri->ptr, qstr - con->request.uri->ptr); } else { buffer_reset (con->uri.query); buffer_copy_buffer(con->uri.path_raw, con->request.uri); } /* decode url to path * * - decode url-encodings (e.g. %20 -> ' ') * - remove path-modifiers (e.g. /../) */ if (con->request.http_method == HTTP_METHOD_OPTIONS && con->uri.path_raw->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { /* OPTIONS * ... */ buffer_copy_buffer(con->uri.path, con->uri.path_raw); } else { buffer_copy_buffer(srv->tmp_buf, con->uri.path_raw); buffer_urldecode_path(srv->tmp_buf); buffer_path_simplify(con->uri.path, srv->tmp_buf); } con->conditional_is_valid[COMP_SERVER_SOCKET] = 1; /* SERVERsocket */ con->conditional_is_valid[COMP_HTTP_SCHEME] = 1; /* Scheme: */ con->conditional_is_valid[COMP_HTTP_HOST] = 1; /* Host: */ con->conditional_is_valid[COMP_HTTP_REMOTE_IP] = 1; /* Client-IP */ con->conditional_is_valid[COMP_HTTP_REFERER] = 1; /* Referer: */ con->conditional_is_valid[COMP_HTTP_USER_AGENT] = /* User-Agent: */ con->conditional_is_valid[COMP_HTTP_LANGUAGE] = 1; /* Accept-Language: */ con->conditional_is_valid[COMP_HTTP_COOKIE] = 1; /* Cookie: */ con->conditional_is_valid[COMP_HTTP_REQUEST_METHOD] = 1; /* REQUEST_METHOD */ con->conditional_is_valid[COMP_HTTP_URL] = 1; /* HTTPurl */ con->conditional_is_valid[COMP_HTTP_QUERY_STRING] = 1; /* HTTPqs */ config_patch_connection(srv, con); #ifdef USE_OPENSSL if (con->srv_socket->is_ssl && con->conf.ssl_verifyclient) { https_add_ssl_entries(con); } #endif /* do we have to downgrade to 1.0 ? */ if (!con->conf.allow_http11) { con->request.http_version = HTTP_VERSION_1_0; } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- splitting Request-URI"); log_error_write(srv, __FILE__, __LINE__, "sb", "Request-URI : ", con->request.uri); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-scheme : ", con->uri.scheme); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-authority : ", con->uri.authority); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path (raw) : ", con->uri.path_raw); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path (clean): ", con->uri.path); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-query : ", con->uri.query); } /** * * call plugins * * - based on the raw URL * */ switch(r = plugins_call_handle_uri_raw(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: return r; default: log_error_write(srv, __FILE__, __LINE__, "sd", "handle_uri_raw: unknown return value", r); break; } /** * * call plugins * * - based on the clean URL * */ switch(r = plugins_call_handle_uri_clean(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: return r; default: log_error_write(srv, __FILE__, __LINE__, ""); break; } if (con->request.http_method == HTTP_METHOD_OPTIONS && con->uri.path->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { /* option requests are handled directly without checking of the path */ response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->http_status = 200; con->file_finished = 1; return HANDLER_FINISHED; } /*** * * border * * logical filename (URI) becomes a physical filename here * * * */ /* 1. stat() * ... ISREG() -> ok, go on * ... ISDIR() -> index-file -> redirect * * 2. pathinfo() * ... ISREG() * * 3. -> 404 * */ /* * SEARCH DOCUMENT ROOT */ /* set a default */ buffer_copy_buffer(con->physical.doc_root, con->conf.document_root); buffer_copy_buffer(con->physical.rel_path, con->uri.path); #if defined(__WIN32) || defined(__CYGWIN__) /* strip dots from the end and spaces * * windows/dos handle those filenames as the same file * * foo == foo. == foo..... == "foo... " == "foo.. ./" * * This will affect in some cases PATHINFO * * on native windows we could prepend the filename with \\?\ to circumvent * this behaviour. I have no idea how to push this through cygwin * * */ if (con->physical.rel_path->used > 1) { buffer *b = con->physical.rel_path; size_t len = buffer_string_length(b); /* strip trailing " /" or "./" once */ if (len > 1 && b->ptr[len - 1] == '/' && (b->ptr[len - 2] == ' ' || b->ptr[len - 2] == '.')) { len -= 2; } /* strip all trailing " " and "." */ while (len > 0 && ( ' ' == b->ptr[len-1] || '.' == b->ptr[len-1] ) ) --len; buffer_string_set_length(b, len); } #endif if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- before doc_root"); log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } /* the docroot plugin should set the doc_root and might also set the physical.path * for us (all vhost-plugins are supposed to set the doc_root) * */ switch(r = plugins_call_handle_docroot(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: return r; default: log_error_write(srv, __FILE__, __LINE__, ""); break; } /* MacOS X and Windows can't distiguish between upper and lower-case * * convert to lower-case */ if (con->conf.force_lowercase_filenames) { buffer_to_lower(con->physical.rel_path); } /* the docroot plugins might set the servername, if they don't we take http-host */ if (buffer_string_is_empty(con->server_name)) { buffer_copy_buffer(con->server_name, con->uri.authority); } /** * create physical filename * -> physical.path = docroot + rel_path * */ buffer_copy_buffer(con->physical.basedir, con->physical.doc_root); buffer_copy_buffer(con->physical.path, con->physical.doc_root); buffer_append_slash(con->physical.path); if (!buffer_string_is_empty(con->physical.rel_path) && con->physical.rel_path->ptr[0] == '/') { buffer_append_string_len(con->physical.path, con->physical.rel_path->ptr + 1, buffer_string_length(con->physical.rel_path) - 1); } else { buffer_append_string_buffer(con->physical.path, con->physical.rel_path); } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- after doc_root"); log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } switch(r = plugins_call_handle_physical(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: return r; default: log_error_write(srv, __FILE__, __LINE__, ""); break; } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- logical -> physical"); log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); log_error_write(srv, __FILE__, __LINE__, "sb", "Basedir :", con->physical.basedir); log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } } /* * Noone catched away the file from normal path of execution yet (like mod_access) * * Go on and check of the file exists at all */ if (con->mode == DIRECT) { char *slash = NULL; char *pathinfo = NULL; int found = 0; stat_cache_entry *sce = NULL; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling physical path"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { /* file exists */ if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- file found"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } #ifdef HAVE_LSTAT if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { con->http_status = 403; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; }; #endif if (S_ISDIR(sce->st.st_mode)) { if (con->uri.path->ptr[buffer_string_length(con->uri.path) - 1] != '/') { /* redirect to .../ */ http_response_redirect_to_directory(srv, con); return HANDLER_FINISHED; } #ifdef HAVE_LSTAT } else if (!S_ISREG(sce->st.st_mode) && !sce->is_symlink) { #else } else if (!S_ISREG(sce->st.st_mode)) { #endif /* any special handling of non-reg files ?*/ } } else { switch (errno) { case EACCES: con->http_status = 403; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; case ENAMETOOLONG: /* file name to be read was too long. return 404 */ case ENOENT: con->http_status = 404; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- file not found"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; case ENOTDIR: /* PATH_INFO ! :) */ break; default: /* we have no idea what happend. let's tell the user so. */ con->http_status = 500; buffer_reset(con->physical.path); log_error_write(srv, __FILE__, __LINE__, "ssbsb", "file not found ... or so: ", strerror(errno), con->uri.path, "->", con->physical.path); return HANDLER_FINISHED; } /* not found, perhaps PATHINFO */ buffer_copy_buffer(srv->tmp_buf, con->physical.path); do { if (slash) { buffer_copy_string_len(con->physical.path, srv->tmp_buf->ptr, slash - srv->tmp_buf->ptr); } else { buffer_copy_buffer(con->physical.path, srv->tmp_buf); } if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { found = S_ISREG(sce->st.st_mode); break; } if (pathinfo != NULL) { *pathinfo = '\0'; } slash = strrchr(srv->tmp_buf->ptr, '/'); if (pathinfo != NULL) { /* restore '/' */ *pathinfo = '/'; } if (slash) pathinfo = slash; } while ((found == 0) && (slash != NULL) && ((size_t)(slash - srv->tmp_buf->ptr) > (buffer_string_length(con->physical.basedir) - 1))); if (found == 0) { /* no it really doesn't exists */ con->http_status = 404; if (con->conf.log_file_not_found) { log_error_write(srv, __FILE__, __LINE__, "sbsb", "file not found:", con->uri.path, "->", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; } #ifdef HAVE_LSTAT if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { con->http_status = 403; if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } buffer_reset(con->physical.path); return HANDLER_FINISHED; }; #endif /* we have a PATHINFO */ if (pathinfo) { size_t len = strlen(pathinfo), reqlen; if (con->conf.force_lowercase_filenames && len <= (reqlen = buffer_string_length(con->request.uri)) && 0 == strncasecmp(con->request.uri->ptr + reqlen - len, pathinfo, len)) { /* attempt to preserve case-insensitive PATH_INFO * (works in common case where mod_alias, mod_magnet, and other modules * have not modified the PATH_INFO portion of request URI, or did so * with exactly the PATH_INFO desired) */ buffer_copy_string_len(con->request.pathinfo, con->request.uri->ptr + reqlen - len, len); } else { buffer_copy_string_len(con->request.pathinfo, pathinfo, len); } /* * shorten uri.path */ buffer_string_set_length(con->uri.path, buffer_string_length(con->uri.path) - len); } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- after pathinfo check"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path); log_error_write(srv, __FILE__, __LINE__, "sb", "Pathinfo :", con->request.pathinfo); } } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling subrequest"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } /* call the handlers */ switch(r = plugins_call_handle_subrequest_start(srv, con)) { case HANDLER_GO_ON: /* request was not handled */ break; case HANDLER_FINISHED: default: if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- subrequest finished"); } /* something strange happend */ return r; } /* if we are still here, no one wanted the file, status 403 is ok I think */ if (con->mode == DIRECT && con->http_status == 0) { switch (con->request.http_method) { case HTTP_METHOD_OPTIONS: con->http_status = 200; break; default: con->http_status = 403; } return HANDLER_FINISHED; } } switch(r = plugins_call_handle_subrequest(srv, con)) { case HANDLER_GO_ON: /* request was not handled, looks like we are done */ return HANDLER_FINISHED; case HANDLER_FINISHED: /* request is finished */ default: /* something strange happend */ return r; } /* can't happen */ return HANDLER_COMEBACK; }
int open_logfile_or_pipe(server *srv, const char* logfile) { int fd; if (logfile[0] == '|') { #ifdef HAVE_FORK /* create write pipe and spawn process */ int to_log_fds[2]; pid_t pid; if (pipe(to_log_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); return -1; } /* fork, execve */ switch (pid = fork()) { case 0: /* child */ close(STDIN_FILENO); /* dup the filehandle to STDIN */ if (to_log_fds[0] != STDIN_FILENO) { if (STDIN_FILENO != dup2(to_log_fds[0], STDIN_FILENO)) { log_error_write(srv, __FILE__, __LINE__, "ss", "dup2 failed: ", strerror(errno)); exit(-1); } close(to_log_fds[0]); } close(to_log_fds[1]); #ifndef FD_CLOEXEC { int i; /* we don't need the client socket */ for (i = 3; i < 256; i++) { close(i); } } #endif /* close old stderr */ openDevNull(STDERR_FILENO); /* exec the log-process (skip the | ) */ execl("/bin/sh", "sh", "-c", logfile + 1, NULL); log_error_write(srv, __FILE__, __LINE__, "sss", "spawning log process failed: ", strerror(errno), logfile + 1); exit(-1); break; case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno)); return -1; default: close(to_log_fds[0]); fd = to_log_fds[1]; break; } #else return -1; #endif } else if (-1 == (fd = open(logfile, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { log_error_write(srv, __FILE__, __LINE__, "SSSS", "opening errorlog '", logfile, "' failed: ", strerror(errno)); return -1; } #ifdef FD_CLOEXEC fcntl(fd, F_SETFD, FD_CLOEXEC); #endif return fd; }