static int magnet_env_next(lua_State *L) { server *srv = magnet_get_server(L); connection *con = magnet_get_connection(L); const int pos = lua_tointeger(L, lua_upvalueindex(1)); buffer *dest; /* ignore previous key: use upvalue for current pos */ lua_settop(L, 0); if (NULL == magnet_env[pos].name) return 0; /* end of list */ /* Update our positional upval to reflect our new current position */ lua_pushinteger(L, pos + 1); lua_replace(L, lua_upvalueindex(1)); /* key to return */ lua_pushstring(L, magnet_env[pos].name); /* get value */ dest = magnet_env_get_buffer_by_id(srv, con, magnet_env[pos].type); if (!buffer_is_empty(dest)) { lua_pushlstring(L, CONST_BUF_LEN(dest)); } else { lua_pushnil(L); } /* return 2 items on the stack (key, value) */ return 2; }
static int http_chunk_append_to_tempfile(server *srv, connection *con, const char * mem, size_t len) { chunkqueue * const cq = con->write_queue; if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { /*http_chunk_append_len(srv, con, len);*/ buffer *b = srv->tmp_chunk_len; buffer_string_set_length(b, 0); buffer_append_uint_hex(b, len); buffer_append_string_len(b, CONST_STR_LEN("\r\n")); if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_BUF_LEN(b))) { return -1; } } if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, mem, len)) { return -1; } if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_STR_LEN("\r\n"))) { return -1; } } return 0; }
static int magnet_env_get(lua_State *L) { server *srv; connection *con; const char *key = luaL_checkstring(L, 2); buffer *dest = NULL; lua_pushstring(L, "lighty.srv"); lua_gettable(L, LUA_REGISTRYINDEX); srv = lua_touserdata(L, -1); lua_pop(L, 1); lua_pushstring(L, "lighty.con"); lua_gettable(L, LUA_REGISTRYINDEX); con = lua_touserdata(L, -1); lua_pop(L, 1); dest = magnet_env_get_buffer(srv, con, key); if (!buffer_is_empty(dest)) { lua_pushlstring(L, CONST_BUF_LEN(dest)); } else { lua_pushnil(L); } return 1; }
/* Define a function that will iterate over an array* (in upval 1) using current position (upval 2) */ static int magnet_array_next(lua_State *L) { data_unset *du; data_string *ds; data_integer *di; size_t pos = lua_tointeger(L, lua_upvalueindex(1)); array *a = lua_touserdata(L, lua_upvalueindex(2)); lua_settop(L, 0); if (pos >= a->used) return 0; if (NULL != (du = a->data[pos])) { lua_pushlstring(L, CONST_BUF_LEN(du->key)); switch (du->type) { case TYPE_STRING: ds = (data_string *)du; if (!buffer_is_empty(ds->value)) { lua_pushlstring(L, CONST_BUF_LEN(ds->value)); } else { lua_pushnil(L); } break; case TYPE_COUNT: case TYPE_INTEGER: di = (data_integer *)du; lua_pushinteger(L, di->value); break; default: lua_pushnil(L); break; } /* Update our positional upval to reflect our new current position */ pos++; lua_pushinteger(L, pos); lua_replace(L, lua_upvalueindex(1)); /* Returning 2 items on the stack (key, value) */ return 2; } return 0; }
static void http_list_directory_footer(server *srv, connection *con, plugin_data *p, buffer *out) { UNUSED(srv); buffer_append_string_len(out, CONST_STR_LEN( "</tbody>\n" "</table>\n" "</div>\n" )); if (!buffer_string_is_empty(p->conf.show_readme)) { /* if we have a README file, display it in <pre class="readme"></pre> */ buffer *rb = p->conf.show_readme; if (rb->ptr[0] != '/') { buffer_copy_buffer(p->tmp_buf, con->physical.path); buffer_append_path_len(p->tmp_buf, CONST_BUF_LEN(p->conf.show_readme)); rb = p->tmp_buf; } http_list_directory_include_file(out, con->conf.follow_symlink, rb, "readme", p->conf.encode_readme); } if(p->conf.auto_layout) { buffer_append_string_len(out, CONST_STR_LEN( "<div class=\"foot\">" )); if (!buffer_string_is_empty(p->conf.set_footer)) { buffer_append_string_buffer(out, p->conf.set_footer); } else { buffer_append_string_buffer(out, con->conf.server_tag); } buffer_append_string_len(out, CONST_STR_LEN( "</div>\n" )); if (!buffer_string_is_empty(p->conf.external_js)) { buffer_append_string_len(out, CONST_STR_LEN("<script type=\"text/javascript\" src=\"")); buffer_append_string_buffer(out, p->conf.external_js); buffer_append_string_len(out, CONST_STR_LEN("\"></script>\n")); } else if (buffer_is_empty(p->conf.external_js)) { http_dirlist_append_js_table_resort(out, con); } buffer_append_string_len(out, CONST_STR_LEN( "</body>\n" "</html>\n" )); } }
static void log_write(server *srv, buffer *b) { switch(srv->errorlog_mode) { case ERRORLOG_PIPE: case ERRORLOG_FILE: case ERRORLOG_FD: buffer_append_string_len(b, CONST_STR_LEN("\n")); write_all(srv->errorlog_fd, CONST_BUF_LEN(b)); break; case ERRORLOG_SYSLOG: syslog(LOG_ERR, "%s", b->ptr); break; } }
static int magnet_cgi_get(lua_State *L) { connection *con = magnet_get_connection(L); data_string *ds; /* __index: param 1 is the (empty) table the value was not found in */ const char *key = luaL_checkstring(L, 2); ds = (data_string *)array_get_element(con->environment, key); if (NULL != ds && !buffer_is_empty(ds->value)) lua_pushlstring(L, CONST_BUF_LEN(ds->value)); else lua_pushnil(L); return 1; }
static int magnet_reqhdr_get(lua_State *L) { connection *con = magnet_get_connection(L); data_string *ds; /* __index: param 1 is the (empty) table the value was not found in */ const char *key = luaL_checkstring(L, 2); if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key))) { if (!buffer_is_empty(ds->value)) { lua_pushlstring(L, CONST_BUF_LEN(ds->value)); } else { lua_pushnil(L); } } else { lua_pushnil(L); } return 1; }
static int magnet_env_get(lua_State *L) { server *srv = magnet_get_server(L); connection *con = magnet_get_connection(L); /* __index: param 1 is the (empty) table the value was not found in */ const char *key = luaL_checkstring(L, 2); buffer *dest = NULL; dest = magnet_env_get_buffer(srv, con, key); if (!buffer_is_empty(dest)) { lua_pushlstring(L, CONST_BUF_LEN(dest)); } else { lua_pushnil(L); } return 1; }
static int magnet_cgi_get(lua_State *L) { connection *con; data_string *ds; const char *key = luaL_checkstring(L, 2); lua_pushstring(L, "lighty.con"); lua_gettable(L, LUA_REGISTRYINDEX); con = lua_touserdata(L, -1); lua_pop(L, 1); if (NULL != (ds = (data_string *)array_get_element(con->environment, key)) && ds->value->used) lua_pushlstring(L, CONST_BUF_LEN(ds->value)); else lua_pushnil(L); return 1; }
void smbc_wrapper_response_401(server *srv, connection *con) { data_string *ds = (data_string *)array_get_element(con->request.headers, "user-Agent"); //- Browser response if( ds && (strstr( ds->value->ptr, "Mozilla" )||strstr( ds->value->ptr, "Opera" )) ){ if(con->mode == SMB_BASIC||con->mode == DIRECT){ Cdbg(DBE, "con->mode == SMB_BASIC -> return 401"); con->http_status = 401; return; } } Cdbg(DBE, "smbc_wrapper_response_401 -> return 401"); char str[50]; UNUSED(srv); buffer* tmp_buf = buffer_init(); if(con->mode == SMB_BASIC){ //sprintf(str, "Basic realm=\"%s\"", "smbdav"); if(con->smb_info&&con->smb_info->server->used) sprintf(str, "Basic realm=\"smb://%s\"", con->smb_info->server->ptr); else sprintf(str, "Basic realm=\"%s\"", "webdav"); } else if(con->mode == SMB_NTLM) sprintf(str, "NTLM"); else sprintf(str, "Basic realm=\"%s\"", "webdav"); buffer_copy_string(tmp_buf, str); response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(tmp_buf)); con->http_status = 401; buffer_free(tmp_buf); }
static void connection_handle_errdoc_init(server *srv, connection *con) { /* modules that produce headers required with error response should * typically also produce an error document. Make an exception for * mod_auth WWW-Authenticate response header. */ buffer *www_auth = NULL; if (401 == con->http_status) { data_string *ds = (data_string *)array_get_element(con->response.headers, "WWW-Authenticate"); if (NULL != ds) { www_auth = buffer_init_buffer(ds->value); } } con->response.transfer_encoding = 0; buffer_reset(con->physical.path); array_reset(con->response.headers); chunkqueue_reset(con->write_queue); if (NULL != www_auth) { response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(www_auth)); buffer_free(www_auth); } }
static int magnet_reqhdr_get(lua_State *L) { connection *con; data_string *ds; const char *key = luaL_checkstring(L, 2); lua_pushstring(L, "lighty.con"); lua_gettable(L, LUA_REGISTRYINDEX); con = lua_touserdata(L, -1); lua_pop(L, 1); if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key))) { if (!buffer_is_empty(ds->value)) { lua_pushlstring(L, CONST_BUF_LEN(ds->value)); } else { lua_pushnil(L); } } else { lua_pushnil(L); } return 1; }
static int magnet_env_next(lua_State *L) { server *srv; connection *con; int pos = lua_tointeger(L, lua_upvalueindex(1)); buffer *dest; lua_pushstring(L, "lighty.srv"); lua_gettable(L, LUA_REGISTRYINDEX); srv = lua_touserdata(L, -1); lua_pop(L, 1); lua_pushstring(L, "lighty.con"); lua_gettable(L, LUA_REGISTRYINDEX); con = lua_touserdata(L, -1); lua_pop(L, 1); lua_settop(L, 0); if (NULL == magnet_env[pos].name) return 0; /* end of list */ lua_pushstring(L, magnet_env[pos].name); dest = magnet_env_get_buffer_by_id(srv, con, magnet_env[pos].type); if (!buffer_is_empty(dest)) { lua_pushlstring(L, CONST_BUF_LEN(dest)); } else { lua_pushnil(L); } /* Update our positional upval to reflect our new current position */ pos++; lua_pushinteger(L, pos); lua_replace(L, lua_upvalueindex(1)); /* Returning 2 items on the stack (key, value) */ return 2; }
void smbc_wrapper_response_realm_401(server *srv, connection *con) { /* if(con->mode == SMB_BASIC){ if(con->smb_info&&con->smb_info->server->used){ Cdbg(DBE, "sssssssss"); con->http_status = 401; } return; } */ char str[50]; UNUSED(srv); buffer* tmp_buf = buffer_init(); if(con->mode == SMB_BASIC){ //sprintf(str, "Basic realm=\"%s\"", "smbdav"); if(con->smb_info&&con->smb_info->server->used) sprintf(str, "Basic realm=\"smb://%s\"", con->smb_info->server->ptr); else sprintf(str, "Basic realm=\"%s\"", "webdav"); } else if(con->mode == SMB_NTLM) sprintf(str, "NTLM"); else sprintf(str, "Basic realm=\"%s\"", "webdav"); buffer_copy_string(tmp_buf, str); response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(tmp_buf)); con->http_status = 401; buffer_free(tmp_buf); }
static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) { buffer *l; server_socket *srv_sock = con->srv_socket; /* check parent first */ if (dc->parent && dc->parent->context_ndx) { /** * a nested conditional * * if the parent is not decided yet or false, we can't be true either */ if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "sb", "go parent", dc->parent->key); } switch (config_check_cond_cached(srv, con, dc->parent)) { case COND_RESULT_FALSE: return COND_RESULT_FALSE; case COND_RESULT_UNSET: return COND_RESULT_UNSET; default: break; } } if (dc->prev) { /** * a else branch * * we can only be executed, if all of our previous brothers * are false */ if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "sb", "go prev", dc->prev->key); } /* make sure prev is checked first */ config_check_cond_cached(srv, con, dc->prev); /* one of prev set me to FALSE */ switch (con->cond_cache[dc->context_ndx].result) { case COND_RESULT_FALSE: return con->cond_cache[dc->context_ndx].result; default: break; } } if (!con->conditional_is_valid[dc->comp]) { if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "dss", dc->comp, dc->key->ptr, con->conditional_is_valid[dc->comp] ? "yeah" : "nej"); } return COND_RESULT_UNSET; } /* pass the rules */ switch (dc->comp) { case COMP_HTTP_HOST: { char *ck_colon = NULL, *val_colon = NULL; if (!buffer_string_is_empty(con->uri.authority)) { /* * append server-port to the HTTP_POST if necessary */ l = con->uri.authority; switch(dc->cond) { case CONFIG_COND_NE: case CONFIG_COND_EQ: ck_colon = strchr(dc->string->ptr, ':'); val_colon = strchr(l->ptr, ':'); if (NULL != ck_colon && NULL == val_colon) { /* condition "host:port" but client send "host" */ buffer_copy_buffer(srv->cond_check_buf, l); buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":")); buffer_append_int(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr))); l = srv->cond_check_buf; } else if (NULL != val_colon && NULL == ck_colon) { /* condition "host" but client send "host:port" */ buffer_copy_string_len(srv->cond_check_buf, l->ptr, val_colon - l->ptr); l = srv->cond_check_buf; } break; default: break; } #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT } else if (!buffer_string_is_empty(con->tlsext_server_name)) { l = con->tlsext_server_name; #endif } else { l = srv->empty_string; } break; } case COMP_HTTP_REMOTE_IP: { char *nm_slash; /* handle remoteip limitations * * "10.0.0.1" is provided for all comparisions * * only for == and != we support * * "10.0.0.1/24" */ if ((dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE) && (con->dst_addr.plain.sa_family == AF_INET) && (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) { int nm_bits; long nm; char *err; struct in_addr val_inp; if (*(nm_slash+1) == '\0') { log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string); return COND_RESULT_FALSE; } nm_bits = strtol(nm_slash + 1, &err, 10); if (*err) { log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, err); return COND_RESULT_FALSE; } if (nm_bits > 32 || nm_bits < 0) { log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: invalid netmask:", dc->string, err); return COND_RESULT_FALSE; } /* take IP convert to the native */ buffer_copy_string_len(srv->cond_check_buf, dc->string->ptr, nm_slash - dc->string->ptr); #ifdef __WIN32 if (INADDR_NONE == (val_inp.s_addr = inet_addr(srv->cond_check_buf->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); return COND_RESULT_FALSE; } #else if (0 == inet_aton(srv->cond_check_buf->ptr, &val_inp)) { log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); return COND_RESULT_FALSE; } #endif /* build netmask */ nm = nm_bits ? htonl(~((1 << (32 - nm_bits)) - 1)) : 0; if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) { return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } } else { l = con->dst_addr_buf; } break; } case COMP_HTTP_SCHEME: l = con->uri.scheme; break; case COMP_HTTP_URL: l = con->uri.path; break; case COMP_HTTP_QUERY_STRING: l = con->uri.query; break; case COMP_SERVER_SOCKET: l = srv_sock->srv_token; break; case COMP_HTTP_REFERER: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) { l = ds->value; } else { l = srv->empty_string; } break; } case COMP_HTTP_COOKIE: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) { l = ds->value; } else { l = srv->empty_string; } break; } case COMP_HTTP_USER_AGENT: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) { l = ds->value; } else { l = srv->empty_string; } break; } case COMP_HTTP_REQUEST_METHOD: { const char *method = get_http_method_name(con->request.http_method); /* we only have the request method as const char but we need a buffer for comparing */ buffer_copy_string(srv->tmp_buf, method); l = srv->tmp_buf; break; } case COMP_HTTP_LANGUAGE: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Accept-Language"))) { l = ds->value; } else { l = srv->empty_string; } break; } default: return COND_RESULT_FALSE; } if (NULL == l) { if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "bsbs", dc->comp_key, "(", l, ") compare to NULL"); } return COND_RESULT_FALSE; } if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "bsbsb", dc->comp_key, "(", l, ") compare to ", dc->string); } switch(dc->cond) { case CONFIG_COND_NE: case CONFIG_COND_EQ: if (buffer_is_equal(l, dc->string)) { return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } break; #ifdef HAVE_PCRE_H case CONFIG_COND_NOMATCH: case CONFIG_COND_MATCH: { cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; int n; #ifndef elementsof #define elementsof(x) (sizeof(x) / sizeof(x[0])) #endif n = pcre_exec(dc->regex, dc->regex_study, CONST_BUF_LEN(l), 0, 0, cache->matches, elementsof(cache->matches)); cache->patterncount = n; if (n > 0) { cache->comp_value = l; cache->comp_type = dc->comp; return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { /* cache is already cleared */ return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } break; } #endif default: /* no way */ break; } return COND_RESULT_FALSE; }
static int connection_handle_write_prepare(server *srv, connection *con) { if (con->mode == DIRECT) { /* static files */ switch(con->request.http_method) { case HTTP_METHOD_GET: case HTTP_METHOD_POST: case HTTP_METHOD_HEAD: break; case HTTP_METHOD_OPTIONS: /* * 400 is coming from the request-parser BEFORE uri.path is set * 403 is from the response handler when noone else catched it * * */ if ((!con->http_status || con->http_status == 200) && !buffer_string_is_empty(con->uri.path) && con->uri.path->ptr[0] != '*') { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; con->http_status = 200; con->file_finished = 1; chunkqueue_reset(con->write_queue); } break; default: if (0 == con->http_status) { con->http_status = 501; } break; } } if (con->http_status == 0) { con->http_status = 403; } switch(con->http_status) { case 204: /* class: header only */ case 205: case 304: /* disable chunked encoding again as we have no body */ con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; chunkqueue_reset(con->write_queue); con->file_finished = 1; break; default: /* class: header + body */ if (con->mode != DIRECT) break; /* only custom body for 4xx and 5xx */ if (con->http_status < 400 || con->http_status >= 600) break; con->file_finished = 0; connection_handle_errdoc_init(srv, con); /* try to send static errorfile */ if (!buffer_string_is_empty(con->conf.errorfile_prefix)) { stat_cache_entry *sce = NULL; buffer_copy_buffer(con->physical.path, con->conf.errorfile_prefix); buffer_append_int(con->physical.path, con->http_status); buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); if (0 == http_chunk_append_file(srv, con, con->physical.path)) { con->file_finished = 1; if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); } } } if (!con->file_finished) { buffer *b; buffer_reset(con->physical.path); con->file_finished = 1; b = buffer_init(); /* build default error-page */ buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" " <title>")); 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)); buffer_append_string_len(b, CONST_STR_LEN( "</title>\n" " </head>\n" " <body>\n" " <h1>")); 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)); buffer_append_string_len(b, CONST_STR_LEN("</h1>\n" " </body>\n" "</html>\n" )); (void)http_chunk_append_buffer(srv, con, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } break; } /* Allow filter plugins to change response headers before they are written. */ switch(plugins_call_handle_response_start(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "s", "response_start plugin failed"); return -1; } if (con->file_finished) { /* we have all the content and chunked encoding is not used, set a content-length */ if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) { off_t qlen = chunkqueue_length(con->write_queue); /** * The Content-Length header only can be sent if we have content: * - HEAD doesn't have a content-body (but have a content-length) * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3) * * Otherwise generate a Content-Length header as chunked encoding is not * available */ if ((con->http_status >= 100 && con->http_status < 200) || con->http_status == 204 || con->http_status == 304) { data_string *ds; /* no Content-Body, no Content-Length */ if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) { buffer_reset(ds->value); /* Headers with empty values are ignored for output */ } } else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) { /* qlen = 0 is important for Redirects (301, ...) as they MAY have * a content. Browsers are waiting for a Content otherwise */ buffer_copy_int(srv->tmp_buf, qlen); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); } } } else { /** * the file isn't finished yet, but we have all headers * * to get keep-alive we either need: * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or * - Transfer-Encoding: chunked (HTTP/1.1) */ if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) && ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) { if (con->request.http_version == HTTP_VERSION_1_1) { off_t qlen = chunkqueue_length(con->write_queue); con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; if (qlen) { /* create initial Transfer-Encoding: chunked segment */ buffer *b = srv->tmp_chunk_len; buffer_string_set_length(b, 0); buffer_append_uint_hex(b, (uintmax_t)qlen); buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_prepend_buffer(con->write_queue, b); chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("\r\n")); } } else { con->keep_alive = 0; } } /** * if the backend sent a Connection: close, follow the wish * * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we * will close the connection. That's fine. We can always decide the close * the connection * * FIXME: to be nice we should remove the Connection: ... */ if (con->parsed_response & HTTP_CONNECTION) { /* a subrequest disable keep-alive although the client wanted it */ if (con->keep_alive && !con->response.keep_alive) { con->keep_alive = 0; } } } if (con->request.http_method == HTTP_METHOD_HEAD) { /** * a HEAD request has the same as a GET * without the content */ con->file_finished = 1; chunkqueue_reset(con->write_queue); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; } http_response_write_header(srv, con); return 0; }
static int magnet_stat(lua_State *L) { buffer *sb = magnet_checkbuffer(L, 1); server *srv = magnet_get_server(L); connection *con = magnet_get_connection(L); stat_cache_entry *sce = NULL; handler_t res; res = stat_cache_get_entry(srv, con, sb, &sce); buffer_free(sb); if (HANDLER_GO_ON != res) { lua_pushnil(L); return 1; } lua_newtable(L); // return value lua_pushboolean(L, S_ISREG(sce->st.st_mode)); lua_setfield(L, -2, "is_file"); lua_pushboolean(L, S_ISDIR(sce->st.st_mode)); lua_setfield(L, -2, "is_dir"); lua_pushboolean(L, S_ISCHR(sce->st.st_mode)); lua_setfield(L, -2, "is_char"); lua_pushboolean(L, S_ISBLK(sce->st.st_mode)); lua_setfield(L, -2, "is_block"); lua_pushboolean(L, S_ISSOCK(sce->st.st_mode)); lua_setfield(L, -2, "is_socket"); lua_pushboolean(L, S_ISLNK(sce->st.st_mode)); lua_setfield(L, -2, "is_link"); lua_pushboolean(L, S_ISFIFO(sce->st.st_mode)); lua_setfield(L, -2, "is_fifo"); lua_pushinteger(L, sce->st.st_mtime); lua_setfield(L, -2, "st_mtime"); lua_pushinteger(L, sce->st.st_ctime); lua_setfield(L, -2, "st_ctime"); lua_pushinteger(L, sce->st.st_atime); lua_setfield(L, -2, "st_atime"); lua_pushinteger(L, sce->st.st_uid); lua_setfield(L, -2, "st_uid"); lua_pushinteger(L, sce->st.st_gid); lua_setfield(L, -2, "st_gid"); lua_pushinteger(L, sce->st.st_size); lua_setfield(L, -2, "st_size"); lua_pushinteger(L, sce->st.st_ino); lua_setfield(L, -2, "st_ino"); if (!buffer_string_is_empty(sce->etag)) { /* we have to mutate the etag */ buffer *b = buffer_init(); etag_mutate(b, sce->etag); lua_pushlstring(L, CONST_BUF_LEN(b)); buffer_free(b); } else { lua_pushnil(L); } lua_setfield(L, -2, "etag"); if (!buffer_string_is_empty(sce->content_type)) { lua_pushlstring(L, CONST_BUF_LEN(sce->content_type)); } else { lua_pushnil(L); } lua_setfield(L, -2, "content-type"); return 1; }
/* lowercase: append space, uppercase: don't */ static void log_buffer_append_printf(buffer *out, const char *fmt, va_list ap) { for(; *fmt; fmt++) { int d; char *s; buffer *b; off_t o; switch(*fmt) { case 's': /* string */ s = va_arg(ap, char *); buffer_append_string_c_escaped(out, s, (NULL != s) ? strlen(s) : 0); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'b': /* buffer */ b = va_arg(ap, buffer *); buffer_append_string_c_escaped(out, CONST_BUF_LEN(b)); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'd': /* int */ d = va_arg(ap, int); buffer_append_int(out, d); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'o': /* off_t */ o = va_arg(ap, off_t); buffer_append_int(out, o); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'x': /* int (hex) */ d = va_arg(ap, int); buffer_append_string_len(out, CONST_STR_LEN("0x")); buffer_append_uint_hex(out, d); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'S': /* string */ s = va_arg(ap, char *); buffer_append_string_c_escaped(out, s, (NULL != s) ? strlen(s) : 0); break; case 'B': /* buffer */ b = va_arg(ap, buffer *); buffer_append_string_c_escaped(out, CONST_BUF_LEN(b)); break; case 'D': /* int */ d = va_arg(ap, int); buffer_append_int(out, d); break; case 'O': /* off_t */ o = va_arg(ap, off_t); buffer_append_int(out, o); break; case 'X': /* int (hex) */ d = va_arg(ap, int); buffer_append_string_len(out, CONST_STR_LEN("0x")); buffer_append_uint_hex(out, d); break; case '(': case ')': case '<': case '>': case ',': case ' ': buffer_append_string_len(out, fmt, 1); break; } } }
static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_data) { #ifdef HAVE_PCRE_H plugin_data *p = p_data; size_t i; /* * REWRITE URL * * e.g. redirect /base/ to /index.php?section=base * */ mod_redirect_patch_connection(srv, con, p); buffer_copy_string_buffer(p->match_buf, con->request.uri); for (i = 0; i < p->conf.redirect->used; i++) { pcre *match; pcre_extra *extra; const char *pattern; size_t pattern_len; int n; pcre_keyvalue *kv = p->conf.redirect->kv[i]; # define N 10 int ovec[N * 3]; match = kv->key; extra = kv->key_extra; pattern = kv->value->ptr; pattern_len = kv->value->used - 1; if ((n = pcre_exec(match, extra, p->match_buf->ptr, p->match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { log_error_write(srv, __FILE__, __LINE__, "sd", "execution error while matching: ", n); return HANDLER_ERROR; } } else { const char **list; size_t start, end; size_t k; /* it matched */ pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list); /* search for $[0-9] */ buffer_reset(p->location); start = 0; end = pattern_len; for (k = 0; k < pattern_len; k++) { if ((pattern[k] == '$' || pattern[k] == '%') && isdigit((unsigned char)pattern[k + 1])) { /* got one */ size_t num = pattern[k + 1] - '0'; end = k; buffer_append_string_len(p->location, pattern + start, end - start); if (pattern[k] == '$') { /* n is always > 0 */ if (num < (size_t)n) { buffer_append_string(p->location, list[num]); } } else { config_append_cond_match_buffer(con, p->conf.context, p->location, num); } k++; start = k + 1; } } buffer_append_string_len(p->location, pattern + start, pattern_len - start); pcre_free(list); response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->location)); con->http_status = 301; con->file_finished = 1; return HANDLER_FINISHED; } } #undef N #else UNUSED(srv); UNUSED(con); UNUSED(p_data); #endif return HANDLER_GO_ON; }
static int cgi_demux_response(server *srv, connection *con, plugin_data *p) { cgi_session *sess = con->plugin_ctx[p->id]; switch(srv->network_backend_read(srv, con, sess->sock, sess->rb)) { case NETWORK_STATUS_CONNECTION_CLOSE: fdevent_event_del(srv->ev, sess->sock); /* connection closed. close the read chunkqueue. */ sess->rb->is_closed = 1; case NETWORK_STATUS_SUCCESS: /* we got content */ break; case NETWORK_STATUS_WAIT_FOR_EVENT: return 0; default: /* oops */ ERROR("%s", "oops, read-pipe-read failed and I don't know why"); return -1; } /* looks like we got some content * * split off the header from the incoming stream */ if (con->file_started == 0) { size_t i; int have_content_length = 0; http_response_reset(p->resp); /* the response header is not fully received yet, * * extract the http-response header from the rb-cq */ switch (http_response_parse_cq(sess->rb, p->resp)) { case PARSE_UNSET: case PARSE_ERROR: /* parsing failed */ TRACE("%s", "response parser failed"); con->http_status = 502; /* Bad Gateway */ return -1; case PARSE_NEED_MORE: if (sess->rb->is_closed) { /* backend died before sending a header */ con->http_status = 502; /* Bad Gateway */ return -1; } return 0; case PARSE_SUCCESS: con->http_status = p->resp->status; chunkqueue_remove_finished_chunks(sess->rb); /* copy the http-headers */ for (i = 0; i < p->resp->headers->used; i++) { const char *ign[] = { "Status", "Connection", NULL }; size_t j; data_string *ds; data_string *header = (data_string *)p->resp->headers->data[i]; /* some headers are ignored by default */ for (j = 0; ign[j]; j++) { if (0 == strcasecmp(ign[j], header->key->ptr)) break; } if (ign[j]) continue; if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Location"))) { /* CGI/1.1 rev 03 - 7.2.1.2 */ if (con->http_status == 0) con->http_status = 302; } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Content-Length"))) { have_content_length = 1; } if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { ds = data_response_init(); } buffer_copy_string_buffer(ds->key, header->key); buffer_copy_string_buffer(ds->value, header->value); array_insert_unique(con->response.headers, (data_unset *)ds); } con->file_started = 1; /* if Status: ... is not set, 200 is our default status-code */ if (con->http_status == 0) con->http_status = 200; sess->state = CGI_STATE_READ_RESPONSE_CONTENT; if (con->request.http_version == HTTP_VERSION_1_1 && !have_content_length) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } break; } } /* FIXME: pass the response-header to the other plugins to * setup the filter-queue * * - use next-queue instead of con->write_queue */ /* copy the resopnse content */ cgi_copy_response(srv, con, sess); joblist_append(srv, con); return 0; }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) { pid_t pid; #ifdef HAVE_IPV6 char b2[INET6_ADDRSTRLEN + 1]; #endif int to_cgi_fds[2]; int from_cgi_fds[2]; struct stat st; #ifndef __WIN32 if (cgi_handler->used > 1) { /* stat the exec file */ if (-1 == (stat(cgi_handler->ptr, &st))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, "failed:", strerror(errno)); return -1; } } if (pipe(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe(from_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } /* fork, execve */ switch (pid = fork()) { case 0: { /* child */ char **args; int argc; int i = 0; char buf[32]; size_t n; char_array env; char *c; const char *s; server_socket *srv_sock = con->srv_socket; /* move stdout to from_cgi_fd[1] */ close(STDOUT_FILENO); dup2(from_cgi_fds[1], STDOUT_FILENO); close(from_cgi_fds[1]); /* not needed */ close(from_cgi_fds[0]); /* move the stdin to to_cgi_fd[0] */ close(STDIN_FILENO); dup2(to_cgi_fds[0], STDIN_FILENO); close(to_cgi_fds[0]); /* not needed */ close(to_cgi_fds[1]); /* HACK: * this is not nice, but it works * * we feed the stderr of the CGI to our errorlog, if possible */ if (srv->errorlog_mode == ERRORLOG_FILE) { close(STDERR_FILENO); dup2(srv->errorlog_fd, STDERR_FILENO); } /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION)); if (!buffer_is_empty(con->server_name)) { cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), CONST_BUF_LEN(con->server_name)); } else { #ifdef HAVE_IPV6 s = inet_ntop(srv_sock->addr.plain.sa_family, srv_sock->addr.plain.sa_family == AF_INET6 ? (const void *) &(srv_sock->addr.ipv6.sin6_addr) : (const void *) &(srv_sock->addr.ipv4.sin_addr), b2, sizeof(b2)-1); #else s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); #endif cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); } cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); s = get_http_version_name(con->request.http_version); cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); ltostr(buf, #ifdef HAVE_IPV6 ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) #else ntohs(srv_sock->addr.ipv4.sin_port) #endif ); cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); #ifdef HAVE_IPV6 s = inet_ntop(srv_sock->addr.plain.sa_family, srv_sock->addr.plain.sa_family == AF_INET6 ? (const void *) &(srv_sock->addr.ipv6.sin6_addr) : (const void *) &(srv_sock->addr.ipv4.sin_addr), b2, sizeof(b2)-1); #else s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); #endif cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); s = get_http_method_name(con->request.http_method); cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); if (!buffer_is_empty(con->request.pathinfo)) { cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); } cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); if (!buffer_is_empty(con->uri.query)) { cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); } if (!buffer_is_empty(con->request.orig_uri)) { cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); } #ifdef HAVE_IPV6 s = inet_ntop(con->dst_addr.plain.sa_family, con->dst_addr.plain.sa_family == AF_INET6 ? (const void *) &(con->dst_addr.ipv6.sin6_addr) : (const void *) &(con->dst_addr.ipv4.sin_addr), b2, sizeof(b2)-1); #else s = inet_ntoa(con->dst_addr.ipv4.sin_addr); #endif cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); ltostr(buf, #ifdef HAVE_IPV6 ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) #else ntohs(con->dst_addr.ipv4.sin_port) #endif ); cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); if (!buffer_is_empty(con->authed_user)) { cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"), CONST_BUF_LEN(con->authed_user)); } /* request.content_length < SSIZE_MAX, see request.c */ ltostr(buf, con->request.content_length); cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); /* for valgrind */ if (NULL != (s = getenv("LD_PRELOAD"))) { cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s)); } if (NULL != (s = getenv("LD_LIBRARY_PATH"))) { cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s)); } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ if (NULL != (s = getenv("SYSTEMROOT"))) { cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s)); } #endif for (n = 0; n < con->request.headers->used; n++) { data_string *ds; ds = (data_string *)con->request.headers->data[n]; if (ds->value->used && ds->key->used) { size_t j; buffer_reset(p->tmp_buf); if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { buffer_copy_string(p->tmp_buf, "HTTP_"); p->tmp_buf->used--; /* strip \0 after HTTP_ */ } buffer_prepare_append(p->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { char cr = '_'; if (light_isalpha(ds->key->ptr[j])) { /* upper-case */ cr = ds->key->ptr[j] & ~32; } else if (light_isdigit(ds->key->ptr[j])) { /* copy */ cr = ds->key->ptr[j]; } p->tmp_buf->ptr[p->tmp_buf->used++] = cr; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } for (n = 0; n < con->environment->used; n++) { data_string *ds; ds = (data_string *)con->environment->data[n]; if (ds->value->used && ds->key->used) { size_t j; buffer_reset(p->tmp_buf); buffer_prepare_append(p->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { p->tmp_buf->ptr[p->tmp_buf->used++] = isalpha((unsigned char)ds->key->ptr[j]) ? toupper((unsigned char)ds->key->ptr[j]) : '_'; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } if (env.size == env.used) { env.size += 16; env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr)); } env.ptr[env.used] = NULL; /* set up args */ argc = 3; args = malloc(sizeof(*args) * argc); i = 0; if (cgi_handler->used > 1) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; args[i++] = NULL; /* search for the last / */ if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { *c = '\0'; /* change to the physical directory */ if (-1 == chdir(con->physical.path->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); } *c = '/'; } /* we don't need the client socket */ for (i = 3; i < 256; i++) { if (i != srv->errorlog_fd) close(i); } /* exec the cgi */ execve(args[0], args, env.ptr); log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); /* */ SEGFAULT(); break; } case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); break; default: { handler_ctx *hctx; /* father */ close(from_cgi_fds[1]); close(to_cgi_fds[0]); if (con->request.content_length) { chunkqueue *cq = con->request_content_queue; chunk *c; assert(chunkqueue_length(cq) == (off_t)con->request.content_length); /* there is content to send */ for (c = cq->first; c; c = cq->first) { int r = 0; /* copy all chunks */ switch(c->type) { case FILE_CHUNK: if (c->file.mmap.start == MAP_FAILED) { if (-1 == c->file.fd && /* open the file if not already open */ -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); close(from_cgi_fds[0]); close(to_cgi_fds[1]); return -1; } c->file.mmap.length = c->file.length; if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", strerror(errno), c->file.name, c->file.fd); close(from_cgi_fds[0]); close(to_cgi_fds[1]); return -1; } close(c->file.fd); c->file.fd = -1; /* chunk_reset() or chunk_free() will cleanup for us */ } if ((r = write(to_cgi_fds[1], c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) { switch(errno) { case ENOSPC: con->http_status = 507; break; default: con->http_status = 403; break; } } break; case MEM_CHUNK: if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) { switch(errno) { case ENOSPC: con->http_status = 507; break; default: con->http_status = 403; break; } } break; case UNUSED_CHUNK: break; } if (r > 0) { c->offset += r; cq->bytes_out += r; } else { break; } chunkqueue_remove_finished_chunks(cq); } } close(to_cgi_fds[1]); /* register PID and wait for them asyncronously */ con->mode = p->id; buffer_reset(con->physical.path); hctx = cgi_handler_ctx_init(); hctx->remote_conn = con; hctx->plugin_data = p; hctx->pid = pid; hctx->fd = from_cgi_fds[0]; hctx->fde_ndx = -1; con->plugin_ctx[p->id] = hctx; fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd); close(hctx->fd); cgi_handler_ctx_free(hctx); con->plugin_ctx[p->id] = NULL; return -1; } break; } } return 0; #else return -1; #endif }
static void handle_fragment(server *srv, connection *con, void *plugindata) { int err; plugin_data *p = plugindata; off_t range_start, range_end; if (copy_bits_session_id_or_set_error(srv, con)) return; err = get_range(srv, con, con->range_offset, &range_start, &range_end); if (range_start > p->state.abs_off || range_end < p->state.abs_off) { //BITS requests must be contiguious - a Transient error may have occured DEBUGLOG("sooo", "The fragment range is greater than abs_off", range_start, p->state.abs_off, range_end); con->http_status = 416; err = -EINVAL; goto done; } if (((con->range_offset + range_start) < p->state.abs_off) && (range_end > p->state.abs_off)){ DEBUGLOG("sooo", "Fragment Overlaps Abs_off:", con->range_offset + range_start, p->state.abs_off, range_end); //Discard bytes we already have: //discard_bytes(srv, con->request_content_queue, p->state.abs_off - (con->range_offset + range_start)+1); //DEBUGLOG("sos", "Discarded", p->state.abs_off -(range_start + con->range_offset), "bytes"); //con->range_offset = p->state.abs_off - range_start; //DEBUGLOG("so", "Setting range offset - ", con->range_offset); p->state.abs_off = con->range_offset + range_start; DEBUGLOG("so", "Re-adjust abs_off", p->state.abs_off); } /* *PREVIOUS IMPLEMENTATION OF 416 - Discards already written data* if ( range_end < p->state.abs_off ) { DEBUGLOG("soo", "The requests range has already been dealt with", range_start, range_end); //Set the range_offset to be the content_length, to return with a healthy 200 http_status con->range_offset = con->request.content_length; goto done; }*/ DEBUGLOG("so", "Range Offset", con->range_offset); DEBUGLOG("soo","Handling Fragment (start/end)", range_start, range_end); if (err) goto done; while (1) { if (chunkqueue_avail(con->request_content_queue) > range_end - range_start + 1 - con->range_offset) { LOG("sdo", "More data than we want!", chunkqueue_avail(con->request_content_queue), range_end - range_start + 1 - con->range_offset); err = -EINVAL; goto done; } err = process_data(srv, con, p, con->physical.path, con->request_content_queue, range_start); if (err) goto done; if (con->range_offset >= range_end - range_start + 1 || chunkqueue_avail(con->request_content_queue) == 0){ DEBUGLOG("sd", "Leaving for more data", chunkqueue_avail(con->request_content_queue)); break; } } done: /* if (con->range_offset == range_end - range_start + 1) { buffer_reset(p->tmpbuf); if (err) buffer_append_off_t(p->tmpbuf, range_start); else buffer_append_off_t(p->tmpbuf, range_end + 1); DEBUGLOG("sdb", "BITS-Received-Content-Range header length", p->tmpbuf->used, p->tmpbuf); response_header_insert(srv, con, CONST_STR_LEN("BITS-Received-Content-Range"), CONST_BUF_LEN(p->tmpbuf)); } */ DEBUGLOG("so", "Abs_off", p->state.abs_off); if (!err) { DEBUGLOG("s", "Resetting HTTP Status"); con->http_status = 0; return; } if (con->http_status != 416 && con->http_status != 400) con->http_status = 400; if (con->http_status = 416) { buffer_reset(p->tmpbuf); buffer_append_off_t(p->tmpbuf, p->state.abs_off); response_header_insert(srv, con, CONST_STR_LEN("BITS-Received-Content-Range"), CONST_BUF_LEN(p->tmpbuf)); } set_error(srv, con, con->http_status, BITS_E_INVALIDARG); }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) { pid_t pid; int to_cgi_fds[2]; int from_cgi_fds[2]; int from_cgi_err_fds[2]; struct stat st; #ifndef _WIN32 if (cgi_handler && cgi_handler->used > 1) { /* stat the exec file */ if (-1 == (stat(cgi_handler->ptr, &st))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, "failed:", strerror(errno)); return -1; } } if (pipe(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe(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; } if (pipe(from_cgi_err_fds)) { close(to_cgi_fds[0]); close(to_cgi_fds[1]); close(from_cgi_fds[0]); close(from_cgi_fds[1]); log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } /* fork, execve */ switch (pid = fork()) { case 0: { /* child */ char **args; int argc; int i = 0; char buf[32]; size_t n; char_array env; char *c; const char *s; server_socket *srv_sock = con->srv_socket; /* move stdout to from_cgi_fd[1] */ close(STDOUT_FILENO); dup2(from_cgi_fds[1], STDOUT_FILENO); close(from_cgi_fds[1]); /* not needed */ close(from_cgi_fds[0]); /* move stderr to from_cgi_err_fd[1] */ close(STDERR_FILENO); dup2(from_cgi_err_fds[1], STDERR_FILENO); close(from_cgi_err_fds[1]); /* not needed */ close(from_cgi_err_fds[0]); /* move the stdin to to_cgi_fd[0] */ close(STDIN_FILENO); dup2(to_cgi_fds[0], STDIN_FILENO); close(to_cgi_fds[0]); /* not needed */ close(to_cgi_fds[1]); /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION)); s = sock_addr_to_p(srv, &srv_sock->addr); cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); /* !!! careful: s maybe reused for SERVER_NAME !!! */ if (!buffer_is_empty(con->server_name)) { size_t len = con->server_name->used - 1; char *colon = strchr(con->server_name->ptr, ':'); if (colon) len = colon - con->server_name->ptr; cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len); } else { /* use SERVER_ADDR */ cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); } cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); s = get_http_version_name(con->request.http_version); cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); LI_ltostr(buf, sock_addr_get_port(&srv_sock->addr)); cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); s = get_http_method_name(con->request.http_method); cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); if (!buffer_is_empty(con->request.pathinfo)) { cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); } cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); if (!buffer_is_empty(con->uri.query)) { cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); } else { /* set a empty QUERY_STRING */ cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")); } if (!buffer_is_empty(con->request.orig_uri)) { cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); } s = sock_addr_to_p(srv, &con->dst_addr); cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); LI_ltostr(buf, sock_addr_get_port(&con->dst_addr)); cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); if (!buffer_is_empty(con->authed_user)) { cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"), CONST_BUF_LEN(con->authed_user)); } #ifdef USE_OPENSSL if (srv_sock->is_ssl) { cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); } #endif /* request.content_length < SSIZE_MAX, see request.c */ if (con->request.content_length > 0) { LI_ltostr(buf, con->request.content_length); cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); } cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); /* for valgrind */ if (NULL != (s = getenv("LD_PRELOAD"))) { cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s)); } if (NULL != (s = getenv("LD_LIBRARY_PATH"))) { cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s)); } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ if (NULL != (s = getenv("SYSTEMROOT"))) { cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s)); } #endif for (n = 0; n < con->request.headers->used; n++) { data_string *ds; ds = (data_string *)con->request.headers->data[n]; if (ds->value->used && ds->key->used) { size_t j; buffer_reset(p->tmp_buf); if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_")); p->tmp_buf->used--; /* strip \0 after HTTP_ */ } buffer_prepare_append(p->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { char cr = '_'; if (light_isalpha(ds->key->ptr[j])) { /* upper-case */ cr = ds->key->ptr[j] & ~32; } else if (light_isdigit(ds->key->ptr[j])) { /* copy */ cr = ds->key->ptr[j]; } p->tmp_buf->ptr[p->tmp_buf->used++] = cr; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } for (n = 0; n < con->environment->used; n++) { data_string *ds; ds = (data_string *)con->environment->data[n]; if (ds->value->used && ds->key->used) { size_t j; buffer_reset(p->tmp_buf); buffer_prepare_append(p->tmp_buf, ds->key->used + 2); for (j = 0; j < ds->key->used - 1; j++) { char cr = '_'; if (light_isalpha(ds->key->ptr[j])) { /* upper-case */ cr = ds->key->ptr[j] & ~32; } else if (light_isdigit(ds->key->ptr[j])) { /* copy */ cr = ds->key->ptr[j]; } p->tmp_buf->ptr[p->tmp_buf->used++] = cr; } p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); } } if (env.size == env.used) { env.size += 16; env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr)); } env.ptr[env.used] = NULL; /* set up args */ argc = 3; args = malloc(sizeof(*args) * argc); i = 0; if (cgi_handler && cgi_handler->used > 1) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; args[i++] = NULL; /* search for the last / */ if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { *c = '\0'; /* change to the physical directory */ if (-1 == chdir(con->physical.path->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); } *c = '/'; } /* we don't need the client socket */ for (i = 3; i < 256; i++) { close(i); } /* exec the cgi */ execve(args[0], args, env.ptr); /* */ SEGFAULT("execve(%s) failed: %s", args[0], strerror(errno)); break; } case -1: /* error */ ERROR("fork() failed: %s", strerror(errno)); close(to_cgi_fds[0]); close(to_cgi_fds[1]); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(from_cgi_err_fds[0]); close(from_cgi_err_fds[1]); return -1; break; default: { cgi_session *sess; /* father */ close(from_cgi_fds[1]); close(from_cgi_err_fds[1]); close(to_cgi_fds[0]); /* register PID and wait for them asyncronously */ con->mode = p->id; buffer_reset(con->physical.path); sess = cgi_session_init(); sess->remote_con = con; sess->pid = pid; assert(sess->sock); sess->sock->fd = from_cgi_fds[0]; sess->sock->type = IOSOCKET_TYPE_PIPE; sess->sock_err->fd = from_cgi_err_fds[0]; sess->sock_err->type = IOSOCKET_TYPE_PIPE; sess->wb_sock->fd = to_cgi_fds[1]; sess->wb_sock->type = IOSOCKET_TYPE_PIPE; if (-1 == fdevent_fcntl_set(srv->ev, sess->sock)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); cgi_session_free(sess); return -1; } if (-1 == fdevent_fcntl_set(srv->ev, sess->sock_err)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); cgi_session_free(sess); return -1; } con->plugin_ctx[p->id] = sess; fdevent_register(srv->ev, sess->sock, cgi_handle_fdevent, sess); fdevent_event_add(srv->ev, sess->sock, FDEVENT_IN); fdevent_register(srv->ev, sess->sock_err, cgi_handle_err_fdevent, sess); fdevent_event_add(srv->ev, sess->sock_err, FDEVENT_IN); sess->state = CGI_STATE_READ_RESPONSE_HEADER; break; } } return 0; #else return -1; #endif }
static void http_list_directory_header(server *srv, connection *con, plugin_data *p, buffer *out) { UNUSED(srv); if (p->conf.auto_layout) { buffer_append_string_len(out, CONST_STR_LEN( "<!DOCTYPE html>\n" "<html>\n" "<head>\n" )); if (!buffer_string_is_empty(p->conf.encoding)) { buffer_append_string_len(out, CONST_STR_LEN("<meta charset=\"")); buffer_append_string_buffer(out, p->conf.encoding); buffer_append_string_len(out, CONST_STR_LEN("\">\n")); } buffer_append_string_len(out, CONST_STR_LEN("<title>Index of ")); buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); buffer_append_string_len(out, CONST_STR_LEN("</title>\n")); if (!buffer_string_is_empty(p->conf.external_css)) { buffer_append_string_len(out, CONST_STR_LEN("<meta name=\"viewport\" content=\"initial-scale=1\">")); buffer_append_string_len(out, CONST_STR_LEN("<link rel=\"stylesheet\" type=\"text/css\" href=\"")); buffer_append_string_buffer(out, p->conf.external_css); buffer_append_string_len(out, CONST_STR_LEN("\">\n")); } else { buffer_append_string_len(out, CONST_STR_LEN( "<style type=\"text/css\">\n" "a, a:active {text-decoration: none; color: blue;}\n" "a:visited {color: #48468F;}\n" "a:hover, a:focus {text-decoration: underline; color: red;}\n" "body {background-color: #F5F5F5;}\n" "h2 {margin-bottom: 12px;}\n" "table {margin-left: 12px;}\n" "th, td {" " font: 90% monospace;" " text-align: left;" "}\n" "th {" " font-weight: bold;" " padding-right: 14px;" " padding-bottom: 3px;" "}\n" "td {padding-right: 14px;}\n" "td.s, th.s {text-align: right;}\n" "div.list {" " background-color: white;" " border-top: 1px solid #646464;" " border-bottom: 1px solid #646464;" " padding-top: 10px;" " padding-bottom: 14px;" "}\n" "div.foot {" " font: 90% monospace;" " color: #787878;" " padding-top: 4px;" "}\n" "</style>\n" )); } buffer_append_string_len(out, CONST_STR_LEN("</head>\n<body>\n")); } if (!buffer_string_is_empty(p->conf.show_header)) { /* if we have a HEADER file, display it in <pre class="header"></pre> */ buffer_copy_buffer(p->tmp_buf, con->physical.path); buffer_append_slash(p->tmp_buf); buffer_append_string_buffer(p->tmp_buf, p->conf.show_header); http_list_directory_include_file(out, p->tmp_buf, "header", p->conf.encode_header); } buffer_append_string_len(out, CONST_STR_LEN("<h2>Index of ")); buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); buffer_append_string_len(out, CONST_STR_LEN( "</h2>\n" "<div class=\"list\">\n" "<table summary=\"Directory Listing\" cellpadding=\"0\" cellspacing=\"0\">\n" "<thead>" "<tr>" "<th class=\"n\">Name</th>" "<th class=\"m\">Last Modified</th>" "<th class=\"s\">Size</th>" "<th class=\"t\">Type</th>" "</tr>" "</thead>\n" "<tbody>\n" "<tr class=\"d\">" "<td class=\"n\"><a href=\"../\">..</a>/</td>" "<td class=\"m\"> </td>" "<td class=\"s\">- </td>" "<td class=\"t\">Directory</td>" "</tr>\n" )); }
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; #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) char attrval[128]; int attrlen; #endif #ifdef HAVE_LOCALTIME_R struct tm tm; #endif if (buffer_string_is_empty(dir)) return -1; i = buffer_string_length(dir); #ifdef HAVE_PATHCONF if (0 >= (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { /* some broken fs (fuse) return 0 instead of -1 */ #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(i + name_max + 1); force_assert(NULL != path); memcpy(path, dir->ptr, i+1); 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); force_assert(dirs.ent); dirs.size = DIRLIST_BLOB_SIZE; dirs.used = 0; files.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); force_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 && !buffer_string_is_empty(p->conf.show_readme)) { if (strcmp(dent->d_name, p->conf.show_readme->ptr) == 0) continue; } if (p->conf.hide_header_file && !buffer_string_is_empty(p->conf.show_header)) { if (strcmp(dent->d_name, p->conf.show_header->ptr) == 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); /* aborting would require a lot of manual cleanup here. * skip instead (to not leak names that break pcre matching) */ exclude_match = 1; break; } } 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); force_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 = buffer_init(); 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 class=\"d\"><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; #if defined(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, srv->srvconf.xattr_name->ptr, attrval, &attrlen, 0) == 0) { attrval[attrlen] = '\0'; content_type = attrval; } } #elif defined(HAVE_EXTATTR) if (con->conf.use_xattr) { memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); if(-1 != (attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, srv->srvconf.xattr_name->ptr, attrval, sizeof(attrval)-1))) { 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 (buffer_is_empty(ds->key)) continue; ct_len = buffer_string_length(ds->key); 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, sizeof(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_string_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; chunkqueue_append_buffer(con->write_queue, out); buffer_free(out); return 0; }
int http_response_write_header(server *srv, connection *con) { buffer *b; size_t i; int have_date = 0; int have_server = 0; b = chunkqueue_get_prepend_buffer(con->write_queue); 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_long(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 (ds->value->used && ds->key->used && 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_prepare_copy(srv->ts_date_str, 255); strftime(srv->ts_date_str->ptr, srv->ts_date_str->size - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts))); srv->ts_date_str->used = strlen(srv->ts_date_str->ptr) + 1; 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 (con->conf.server_tag->used > 1) { 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); } } //- Jerry add 20110923 #if EMBEDDED_EANBLE char * ddns_host_n = nvram_get_ddns_host_name(); if(ddns_host_n){ buffer_append_string_len(b, CONST_STR_LEN("\r\nDDNS: ")); buffer_append_string(b, ddns_host_n); }else #endif buffer_append_string_len(b, CONST_STR_LEN("\r\nDDNS: ")); buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); con->bytes_header = b->used - 1; if (con->conf.log_response_header) { log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b); } return 0; }
int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { lua_State *L; int ret = -1; buffer *b; b = buffer_init(); /* 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 USE_MEMCACHED lua_pushlightuserdata(L, p->conf.memc); lua_pushcclosure(L, f_memcache_get_long, 1); lua_setglobal(L, "memcache_get_long"); lua_pushlightuserdata(L, p->conf.memc); lua_pushcclosure(L, f_memcache_get_string, 1); lua_setglobal(L, "memcache_get_string"); lua_pushlightuserdata(L, p->conf.memc); lua_pushcclosure(L, f_memcache_exists, 1); lua_setglobal(L, "memcache_exists"); #endif /* register CGI environment */ lua_newtable(L); { int header_tbl = lua_gettop(L); 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.basedir)); if (!buffer_string_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)); } lua_setglobal(L, "request"); /* register GET parameter */ lua_newtable(L); { int get_tbl = lua_gettop(L); buffer_copy_buffer(b, con->uri.query); cache_export_get_params(L, get_tbl, b); buffer_reset(b); } lua_setglobal(L, "get"); /* 2 default constants */ lua_pushinteger(L, 0); lua_setglobal(L, "CACHE_HIT"); lua_pushinteger(L, 1); lua_setglobal(L, "CACHE_MISS"); /* load lua program */ ret = luaL_loadfile(L, fn->ptr); if (0 != ret) { log_error_write(srv, __FILE__, __LINE__, "sbsS", "failed loading cml_lua script", fn, ":", lua_tostring(L, -1)); goto error; } if (lua_pcall(L, 0, 1, 0)) { log_error_write(srv, __FILE__, __LINE__, "sbsS", "failed running cml_lua script", fn, ":", lua_tostring(L, -1)); goto error; } /* get return value */ ret = (int)lua_tointeger(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_getglobal(L, "output_include"); curelem = lua_gettop(L); /* 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) { /* key' is at index -2 and value' at index -1 */ if (lua_isstring(L, -1)) { const char *s = lua_tostring(L, -1); struct stat st; int fd; /* the file is relative, make it absolute */ if (s[0] != '/') { buffer_copy_buffer(b, p->basedir); buffer_append_string(b, lua_tostring(L, -1)); } else { buffer_copy_string(b, lua_tostring(L, -1)); } fd = stat_cache_open_rdonly_fstat(srv, con, b, &st); if (fd < 0) { /* stat failed */ switch(errno) { case ENOENT: /* a file is missing, call the handler to generate it */ if (!buffer_string_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_fd(con->write_queue, b, fd, 0, st.st_size); if (st.st_mtime > mtime) mtime = 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")]; con->file_finished = 1; ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"); if (0 == mtime) mtime = time(NULL); /* default last-modified to now */ /* no Last-Modified specified */ if (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); ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"); force_assert(NULL != ds); } if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, ds->value)) { /* 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_string_is_empty(p->trigger_handler)) { /* cache-miss */ buffer_copy_buffer(con->uri.path, p->baseurl); buffer_append_string_buffer(con->uri.path, p->trigger_handler); buffer_copy_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); buffer_free(b); return ret /* cache-error */; }
static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { size_t k; int auth_required = 0, auth_satisfied = 0; char *http_authorization = NULL; data_string *ds; mod_auth_plugin_data *p = p_d; array *req; /* select the right config */ mod_auth_patch_connection(srv, con, p); if (p->conf.auth_require == NULL) return HANDLER_GO_ON; /* * AUTH * */ /* do we have to ask for auth ? */ auth_required = 0; auth_satisfied = 0; /* search auth-directives for path */ for (k = 0; k < p->conf.auth_require->used; k++) { buffer *require = p->conf.auth_require->data[k]->key; if (require->used == 0) continue; if (con->uri.path->used < require->used) continue; /* if we have a case-insensitive FS we have to lower-case the URI here too */ if (con->conf.force_lowercase_filenames) { if (0 == strncasecmp(con->uri.path->ptr, require->ptr, require->used - 1)) { auth_required = 1; break; } } else { if (0 == strncmp(con->uri.path->ptr, require->ptr, require->used - 1)) { auth_required = 1; break; } } } /* nothing to do for us */ if (auth_required == 0) return HANDLER_GO_ON; req = ((data_array *)(p->conf.auth_require->data[k]))->value; /* try to get Authorization-header */ if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Authorization"))) { http_authorization = ds->value->ptr; } if (ds && ds->value && ds->value->used) { char *auth_realm; data_string *method; method = (data_string *)array_get_element(req, "method"); /* parse auth-header */ if (NULL != (auth_realm = strchr(http_authorization, ' '))) { int auth_type_len = auth_realm - http_authorization; if ((auth_type_len == 5) && (0 == strncasecmp(http_authorization, "Basic", auth_type_len))) { if (0 == strcmp(method->value->ptr, "basic")) { auth_satisfied = http_auth_basic_check(srv, con, p, req, con->uri.path, auth_realm+1); } } else if ((auth_type_len == 6) && (0 == strncasecmp(http_authorization, "Digest", auth_type_len))) { if (0 == strcmp(method->value->ptr, "digest")) { if (-1 == (auth_satisfied = http_auth_digest_check(srv, con, p, req, con->uri.path, auth_realm+1))) { con->http_status = 400; con->mode = DIRECT; /* a field was missing */ return HANDLER_FINISHED; } } } else { log_error_write(srv, __FILE__, __LINE__, "ss", "unknown authentification type:", http_authorization); } } } if (!auth_satisfied) { data_string *method, *realm; method = (data_string *)array_get_element(req, "method"); realm = (data_string *)array_get_element(req, "realm"); con->http_status = 401; con->mode = DIRECT; if (0 == strcmp(method->value->ptr, "basic")) { buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Basic realm=\"")); buffer_append_string_buffer(p->tmp_buf, realm->value); buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\"")); response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); } else if (0 == strcmp(method->value->ptr, "digest")) { char hh[33]; http_auth_digest_generate_nonce(srv, p, srv->tmp_buf, hh); buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Digest realm=\"")); buffer_append_string_buffer(p->tmp_buf, realm->value); buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", nonce=\"")); buffer_append_string(p->tmp_buf, hh); buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", qop=\"auth\"")); response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); } else { /* evil */ } return HANDLER_FINISHED; } else { /* the REMOTE_USER header */ buffer_copy_string_buffer(con->authed_user, p->auth_user); } return HANDLER_GO_ON; }
static handler_t mod_status_handle_server_status_html(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; buffer *b; size_t j; double avg; char multiplier = '\0'; char buf[32]; time_t ts; int days, hours, mins, seconds; b = chunkqueue_get_append_buffer(con->write_queue); BUFFER_COPY_STRING_CONST(b, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" " <title>Status</title>\n"); BUFFER_APPEND_STRING_CONST(b, " <style type=\"text/css\">\n" " table.status { border: black solid thin; }\n" " td.int { background-color: #f0f0f0; text-align: right }\n" " td.string { background-color: #f0f0f0; text-align: left }\n" " th.status { background-color: black; color: white; font-weight: bold; }\n" " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n" " span.sortarrow { color: white; text-decoration: none; }\n" " </style>\n"); if (p->conf.sort) { BUFFER_APPEND_STRING_CONST(b, "<script type=\"text/javascript\">\n" "// <!--\n" "var sort_column;\n" "var prev_span = null;\n"); BUFFER_APPEND_STRING_CONST(b, "function get_inner_text(el) {\n" " if((typeof el == 'string')||(typeof el == 'undefined'))\n" " return el;\n" " if(el.innerText)\n" " return el.innerText;\n" " else {\n" " var str = \"\";\n" " var cs = el.childNodes;\n" " var l = cs.length;\n" " for (i=0;i<l;i++) {\n" " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n" " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n" " }\n" " }\n" " return str;\n" "}\n"); BUFFER_APPEND_STRING_CONST(b, "function sortfn(a,b) {\n" " var at = get_inner_text(a.cells[sort_column]);\n" " var bt = get_inner_text(b.cells[sort_column]);\n" " if (a.cells[sort_column].className == 'int') {\n" " return parseInt(at)-parseInt(bt);\n" " } else {\n" " aa = at.toLowerCase();\n" " bb = bt.toLowerCase();\n" " if (aa==bb) return 0;\n" " else if (aa<bb) return -1;\n" " else return 1;\n" " }\n" "}\n"); BUFFER_APPEND_STRING_CONST(b, "function resort(lnk) {\n" " var span = lnk.childNodes[1];\n" " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n" " var rows = new Array();\n" " for (j=1;j<table.rows.length;j++)\n" " rows[j-1] = table.rows[j];\n" " sort_column = lnk.parentNode.cellIndex;\n" " rows.sort(sortfn);\n"); BUFFER_APPEND_STRING_CONST(b, " if (prev_span != null) prev_span.innerHTML = '';\n" " if (span.getAttribute('sortdir')=='down') {\n" " span.innerHTML = '↑';\n" " span.setAttribute('sortdir','up');\n" " rows.reverse();\n" " } else {\n" " span.innerHTML = '↓';\n" " span.setAttribute('sortdir','down');\n" " }\n" " for (i=0;i<rows.length;i++)\n" " table.tBodies[0].appendChild(rows[i]);\n" " prev_span = span;\n" "}\n" "// -->\n" "</script>\n"); } BUFFER_APPEND_STRING_CONST(b, " </head>\n" " <body>\n"); /* connection listing */ BUFFER_APPEND_STRING_CONST(b, "<h1>Server-Status</h1>"); BUFFER_APPEND_STRING_CONST(b, "<table summary=\"status\" class=\"status\">"); BUFFER_APPEND_STRING_CONST(b, "<tr><td>Hostname</td><td class=\"string\">"); buffer_append_string_buffer(b, con->uri.authority); BUFFER_APPEND_STRING_CONST(b, " ("); buffer_append_string_buffer(b, con->server_name); BUFFER_APPEND_STRING_CONST(b, ")</td></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "<tr><td>Uptime</td><td class=\"string\">"); ts = srv->cur_ts - srv->startup_ts; days = ts / (60 * 60 * 24); ts %= (60 * 60 * 24); hours = ts / (60 * 60); ts %= (60 * 60); mins = ts / (60); ts %= (60); seconds = ts; if (days) { buffer_append_long(b, days); BUFFER_APPEND_STRING_CONST(b, " days "); } if (hours) { buffer_append_long(b, hours); BUFFER_APPEND_STRING_CONST(b, " hours "); } if (mins) { buffer_append_long(b, mins); BUFFER_APPEND_STRING_CONST(b, " min "); } buffer_append_long(b, seconds); BUFFER_APPEND_STRING_CONST(b, " s"); BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "<tr><td>Started at</td><td class=\"string\">"); ts = srv->startup_ts; strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts)); buffer_append_string(b, buf); BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">absolute (since start)</th></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">"); avg = p->abs_requests; mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_long(b, avg); BUFFER_APPEND_STRING_CONST(b, " "); if (multiplier) buffer_append_string_len(b, &multiplier, 1); BUFFER_APPEND_STRING_CONST(b, "req</td></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "<tr><td>Traffic</td><td class=\"string\">"); avg = p->abs_traffic_out; mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); BUFFER_APPEND_STRING_CONST(b, " "); if (multiplier) buffer_append_string_len(b, &multiplier, 1); BUFFER_APPEND_STRING_CONST(b, "byte</td></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">average (since start)</th></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">"); avg = p->abs_requests / (srv->cur_ts - srv->startup_ts); mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_long(b, avg); BUFFER_APPEND_STRING_CONST(b, " "); if (multiplier) buffer_append_string_len(b, &multiplier, 1); BUFFER_APPEND_STRING_CONST(b, "req/s</td></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "<tr><td>Traffic</td><td class=\"string\">"); avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts); mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); BUFFER_APPEND_STRING_CONST(b, " "); if (multiplier) buffer_append_string_len(b, &multiplier, 1); BUFFER_APPEND_STRING_CONST(b, "byte/s</td></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n"); for (j = 0, avg = 0; j < 5; j++) { avg += p->mod_5s_requests[j]; } avg /= 5; BUFFER_APPEND_STRING_CONST(b, "<tr><td>Requests</td><td class=\"string\">"); mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_long(b, avg); BUFFER_APPEND_STRING_CONST(b, " "); if (multiplier) buffer_append_string_len(b, &multiplier, 1); BUFFER_APPEND_STRING_CONST(b, "req/s</td></tr>\n"); for (j = 0, avg = 0; j < 5; j++) { avg += p->mod_5s_traffic_out[j]; } avg /= 5; BUFFER_APPEND_STRING_CONST(b, "<tr><td>Traffic</td><td class=\"string\">"); mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); BUFFER_APPEND_STRING_CONST(b, " "); if (multiplier) buffer_append_string_len(b, &multiplier, 1); BUFFER_APPEND_STRING_CONST(b, "byte/s</td></tr>\n"); BUFFER_APPEND_STRING_CONST(b, "</table>\n"); BUFFER_APPEND_STRING_CONST(b, "<hr />\n<pre><b>legend</b>\n"); BUFFER_APPEND_STRING_CONST(b, ". = connect, C = close, E = hard error\n"); BUFFER_APPEND_STRING_CONST(b, "r = read, R = read-POST, W = write, h = handle-request\n"); BUFFER_APPEND_STRING_CONST(b, "q = request-start, Q = request-end\n"); BUFFER_APPEND_STRING_CONST(b, "s = response-start, S = response-end\n"); BUFFER_APPEND_STRING_CONST(b, "<b>"); buffer_append_long(b, srv->conns->used); BUFFER_APPEND_STRING_CONST(b, " connections</b>\n"); for (j = 0; j < srv->conns->used; j++) { connection *c = srv->conns->ptr[j]; const char *state = connection_get_short_state(c->state); buffer_append_string_len(b, state, 1); if (((j + 1) % 50) == 0) { BUFFER_APPEND_STRING_CONST(b, "\n"); } } BUFFER_APPEND_STRING_CONST(b, "\n</pre><hr />\n<h2>Connections</h2>\n"); BUFFER_APPEND_STRING_CONST(b, "<table summary=\"status\" class=\"status\">\n"); BUFFER_APPEND_STRING_CONST(b, "<tr>"); mod_status_header_append_sort(b, p_d, "Client IP"); mod_status_header_append_sort(b, p_d, "Read"); mod_status_header_append_sort(b, p_d, "Written"); mod_status_header_append_sort(b, p_d, "State"); mod_status_header_append_sort(b, p_d, "Time"); mod_status_header_append_sort(b, p_d, "Host"); mod_status_header_append_sort(b, p_d, "URI"); mod_status_header_append_sort(b, p_d, "File"); BUFFER_APPEND_STRING_CONST(b, "</tr>\n"); for (j = 0; j < srv->conns->used; j++) { connection *c = srv->conns->ptr[j]; BUFFER_APPEND_STRING_CONST(b, "<tr><td class=\"string\">"); buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr))); BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">"); if (con->request.content_length) { buffer_append_long(b, c->request_content_queue->bytes_in); BUFFER_APPEND_STRING_CONST(b, "/"); buffer_append_long(b, c->request.content_length); } else { BUFFER_APPEND_STRING_CONST(b, "0/0"); } BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">"); buffer_append_off_t(b, chunkqueue_written(c->write_queue)); BUFFER_APPEND_STRING_CONST(b, "/"); buffer_append_off_t(b, chunkqueue_length(c->write_queue)); BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">"); buffer_append_string(b, connection_get_state(c->state)); BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"int\">"); buffer_append_long(b, srv->cur_ts - c->request_start); BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">"); if (buffer_is_empty(c->server_name)) { buffer_append_string_buffer(b, c->uri.authority); } else { buffer_append_string_buffer(b, c->server_name); } BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">"); if (!buffer_is_empty(c->uri.path)) { buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML); } BUFFER_APPEND_STRING_CONST(b, "</td><td class=\"string\">"); buffer_append_string_buffer(b, c->physical.path); BUFFER_APPEND_STRING_CONST(b, "</td></tr>\n"); } BUFFER_APPEND_STRING_CONST(b, "</table>\n"); BUFFER_APPEND_STRING_CONST(b, " </body>\n" "</html>\n" ); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); return 0; }