static int http_chunk_append_len(server * srv, connection * con, size_t len) { size_t i, olen = len, j; buffer *b; b = srv->tmp_chunk_len; if (len == 0) { buffer_copy_string_len(b, CONST_STR_LEN("0")); } else { for (i = 0; i < 8 && len; i++) { len >>= 4; } /* * i is the number of hex digits we have */ buffer_prepare_copy(b, i + 1); for (j = i - 1, len = olen; j + 1 > 0; j--) { b->ptr[j] = (len & 0xf) + (((len & 0xf) <= 9) ? '0' : 'a' - 10); len >>= 4; } b->used = i; b->ptr[b->used++] = '\0'; } buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_append_buffer(con->write_queue, b); return 0; }
//该函数用于逐个记录那些被转换了的配置信息(记录在srv->config_touched中) int config_insert_values_global(server *srv, array *ca, const config_values_t cv[]) { size_t i; data_unset *du; //对将要被转换的配置值逐个判断以记录被使用了的配置项 for (i = 0; cv[i].key; i++) { data_string *touched; if (NULL == (du = array_get_element(ca, cv[i].key))) { /* no found */ //配置文件里没有对其的配置项 continue; } /* touched */ //有配置 touched = data_string_init(); buffer_copy_string_len(touched->value, CONST_STR_LEN("")); //并不关心其配置值 buffer_copy_string_buffer(touched->key, du->key); //获取其配置项的Key array_insert_unique(srv->config_touched, (data_unset *)touched); //记录被使用的配置项 } return config_insert_values_internal(srv, ca, cv); //调用函数config_insert_values_internal()获取配置值 }
int config_insert_values_global(server *srv, array *ca, const config_values_t cv[]) { size_t i; data_unset *du; for (i = 0; cv[i].key; i++) { data_string *touched; if (NULL == (du = array_get_element(ca, cv[i].key))) { /* no found */ continue; } /* touched */ touched = data_string_init(); buffer_copy_string_len(touched->value, CONST_STR_LEN("")); buffer_copy_string_buffer(touched->key, du->key); array_insert_unique(srv->config_touched, (data_unset *)touched); } return config_insert_values_internal(srv, ca, cv); }
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; }
handler_t http_response_prepare(server *srv, connection *con) { handler_t r; Cdbg(DBE, "enter http_response_prepare..mode=[%d], status=[%d][%s]", con->mode, con->http_status, connection_get_state(con->http_status)); /* looks like someone has already done a decision */ if ( (con->mode == DIRECT || con->mode == SMB_BASIC || con->mode == SMB_NTLM) && (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 || con->mode == SMB_BASIC || con->mode == SMB_NTLM) && con->physical.path->used == 0) { 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"); } config_patch_connection(srv, con, COMP_SERVER_SOCKET); /* SERVERsocket */ /** * prepare strings * * - uri.path_raw * - uri.path (secure) * - uri.query * */ /** * Name according to RFC 2396 * * - scheme * - authority * - path * - query * * (scheme)://(authority)(path)?(query)#fragment * * */ if (con->conf.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_string_buffer(con->uri.authority, con->request.http_host); buffer_to_lower(con->uri.authority); config_patch_connection(srv, con, COMP_HTTP_SCHEME); /* Scheme: */ config_patch_connection(srv, con, COMP_HTTP_HOST); /* Host: */ config_patch_connection(srv, con, COMP_HTTP_REMOTE_IP); /* Client-IP */ config_patch_connection(srv, con, COMP_HTTP_REFERER); /* Referer: */ config_patch_connection(srv, con, COMP_HTTP_USER_AGENT);/* User-Agent: */ config_patch_connection(srv, con, COMP_HTTP_LANGUAGE); /* Accept-Language: */ config_patch_connection(srv, con, COMP_HTTP_COOKIE); /* Cookie: */ config_patch_connection(srv, con, COMP_HTTP_REQUEST_METHOD); /* REQUEST_METHOD */ /** their might be a fragment which has to be cut away */ if (NULL != (qstr = strchr(con->request.uri->ptr, '#'))) { con->request.uri->used = qstr - con->request.uri->ptr; con->request.uri->ptr[con->request.uri->used++] = '\0'; } /** 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_string_buffer(con->uri.path_raw, con->request.uri); } 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 : ", con->uri.path_raw); 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; } /* build filename * * - 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_string_buffer(con->uri.path, con->uri.path_raw); } else { buffer_copy_string_buffer(srv->tmp_buf, con->uri.path_raw); buffer_urldecode_path(srv->tmp_buf); buffer_path_simplify(con->uri.path, srv->tmp_buf); } if (con->conf.log_request_handling) { log_error_write(srv, __FILE__, __LINE__, "s", "-- sanatising URI"); log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path : ", con->uri.path); } #ifdef USE_OPENSSL if (con->conf.is_ssl && con->conf.ssl_verifyclient) { https_add_ssl_entries(con); } #endif /** * * call plugins * * - based on the clean URL * */ config_patch_connection(srv, con, COMP_HTTP_URL); /* HTTPurl */ config_patch_connection(srv, con, COMP_HTTP_QUERY_STRING); /* HTTPqs */ /* do we have to downgrade to 1.0 ? */ if (!con->conf.allow_http11) { con->request.http_version = HTTP_VERSION_1_0; } 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_string_buffer(con->physical.doc_root, con->conf.document_root); buffer_copy_string_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 i; if (b->used > 2 && b->ptr[b->used-2] == '/' && (b->ptr[b->used-3] == ' ' || b->ptr[b->used-3] == '.')) { b->ptr[b->used--] = '\0'; } for (i = b->used - 2; b->used > 1; i--) { if (b->ptr[i] == ' ' || b->ptr[i] == '.') { b->ptr[b->used--] = '\0'; } else { break; } } } #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_is_empty(con->server_name)) { buffer_copy_string_buffer(con->server_name, con->uri.authority); } /** * create physical filename * -> physical.path = docroot + rel_path * */ buffer_copy_string_buffer(con->physical.path, con->physical.doc_root); BUFFER_APPEND_SLASH(con->physical.path); buffer_copy_string_buffer(con->physical.basedir, con->physical.path); if (con->physical.rel_path->used && con->physical.rel_path->ptr[0] == '/') { buffer_append_string_len(con->physical.path, con->physical.rel_path->ptr + 1, con->physical.rel_path->used - 2); //buffer_append_string_encoded(con->physical.path, con->physical.rel_path->ptr + 1, con->physical.rel_path->used - 2, ENCODING_REL_URI); //Cdbg(1,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa %s", con->physical.path->ptr); } 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", "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 || con->mode == SMB_BASIC || con->mode == SMB_NTLM) { 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, smbc_wrapper_physical_url_path(srv, con), &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[con->uri.path->used - 2] != '/') { /* 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 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_string_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_string_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) > (con->physical.basedir->used - 2))); 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) { buffer_copy_string(con->request.pathinfo, pathinfo); /* * shorten uri.path */ con->uri.path->used -= strlen(pathinfo); con->uri.path->ptr[con->uri.path->used - 1] = '\0'; } 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->mode == SMB_BASIC || con->mode == SMB_NTLM) && 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; }
static buffer* magnet_checkbuffer(lua_State *L, int index) { const_buffer cb = magnet_checkconstbuffer(L, index); buffer *b = buffer_init(); buffer_copy_string_len(b, cb.ptr, cb.len); return b; }
static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) { char *ns; const char *s; int line = 0; UNUSED(srv); buffer_copy_buffer(p->parse_response, in); for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1, line++) { const char *key, *value; int key_len; data_string *ds; /* strip the \n */ ns[0] = '\0'; if (ns > s && ns[-1] == '\r') ns[-1] = '\0'; if (line == 0 && 0 == strncmp(s, "HTTP/1.", 7)) { /* non-parsed header ... we parse them anyway */ if ((s[7] == '1' || s[7] == '0') && s[8] == ' ') { int status; /* after the space should be a status code for us */ status = strtol(s+9, NULL, 10); if (status >= 100 && status < 1000) { /* we expected 3 digits and didn't got them */ con->parsed_response |= HTTP_STATUS; con->http_status = status; } } } else { /* parse the headers */ key = s; if (NULL == (value = strchr(s, ':'))) { /* we expect: "<key>: <value>\r\n" */ continue; } key_len = value - key; value += 1; /* skip LWS */ while (*value == ' ' || *value == '\t') value++; if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { ds = data_response_init(); } buffer_copy_string_len(ds->key, key, key_len); buffer_copy_string(ds->value, value); array_insert_unique(con->response.headers, (data_unset *)ds); switch(key_len) { case 4: if (0 == strncasecmp(key, "Date", key_len)) { con->parsed_response |= HTTP_DATE; } break; case 6: if (0 == strncasecmp(key, "Status", key_len)) { int status = strtol(value, NULL, 10); if (status >= 100 && status < 1000) { con->http_status = status; con->parsed_response |= HTTP_STATUS; } else { con->http_status = 502; } } break; case 8: if (0 == strncasecmp(key, "Location", key_len)) { con->parsed_response |= HTTP_LOCATION; } break; case 10: if (0 == strncasecmp(key, "Connection", key_len)) { con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0; con->parsed_response |= HTTP_CONNECTION; } break; case 14: if (0 == strncasecmp(key, "Content-Length", key_len)) { con->response.content_length = strtoul(value, NULL, 10); con->parsed_response |= HTTP_CONTENT_LENGTH; } break; default: break; } } } /* CGI/1.1 rev 03 - 7.2.1.2 */ if ((con->parsed_response & HTTP_LOCATION) && !(con->parsed_response & HTTP_STATUS)) { con->http_status = 302; } return 0; }
static int request_check_hostname(server * srv, connection * con, buffer * host) { enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL; size_t i; int label_len = 0; size_t host_len; char *colon; int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */ int level = 0; UNUSED(srv); UNUSED(con); /* * hostport = host [ ":" port ] * host = hostname | IPv4address | IPv6address * hostname = *( domainlabel "." ) toplabel [ "." ] * domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum * toplabel = alpha | alpha *( alphanum | "-" ) alphanum * IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit * IPv6address = "[" ... "]" //IPv6地址用[]包围. * port = *digit */ /* * no Host: */ if (!host || host->used == 0) return 0; host_len = host->used - 1; /* * IPv6 adress */ if (host->ptr[0] == '[') { char *c = host->ptr + 1; int colon_cnt = 0; /* * check portnumber */ for (; *c && *c != ']'; c++) { if (*c == ':') { //至多7个分号。 if (++colon_cnt > 7) { return -1; } } else if (!light_isxdigit(*c)) { return -1; } } /* * missing ] */ if (!*c) { return -1; } /* * check port */ if (*(c + 1) == ':') { for (c += 2; *c; c++) { if (!light_isdigit(*c)) { return -1; } } } return 0; } if (NULL != (colon = memchr(host->ptr, ':', host_len))) { char *c = colon + 1; /* * check portnumber */ for (; *c; c++) { if (!light_isdigit(*c)) return -1; } /* * remove the port from the host-len */ host_len = colon - host->ptr; } /* * Host is empty */ if (host_len == 0) return -1; /* * if the hostname ends in a "." strip it */ if (host->ptr[host_len - 1] == '.') host_len -= 1; /* * scan from the right and skip the \0 */ for (i = host_len - 1; i + 1 > 0; i--) { const char c = host->ptr[i]; switch (stage) { case TOPLABEL: if (c == '.') { /*/** * header lines中的value,可以是以","分割的多个value。 * 这个函数将v中value,按照","分割成多个值,存放在vals中。 */ int http_request_split_value(array * vals, buffer * b) { char *s; size_t i; int state = 0; /* * parse * val1, val2, val3, val4 * into a array (more or less a explode() incl. striping of whitespaces */ if (b->used == 0) return 0; s = b->ptr; for (i = 0; i < b->used - 1;) { char *start = NULL, *end = NULL; data_string *ds; switch (state) { case 0: /* ws */ /* * skip ws */ for (; (*s == ' ' || *s == '\t') && i < b->used - 1; i++, s++); state = 1; break; case 1: /* value */ start = s; for (; *s != ',' && i < b->used - 1; i++, s++); end = s - 1; //去掉空格 for (; (*end == ' ' || *end == '\t') && end > start; end--); if (NULL == (ds = (data_string *) array_get_unused_element(vals, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string_len(ds->value, start, end - start + 1); array_insert_unique(vals, (data_unset *) ds); if (*s == ',') { state = 0; i++; s++; } else { /* * end of string */ state = 2; } break; default: i++; break; } } return 0; } * only switch stage, if this is not the last character */ if (i != host_len - 1) { if (label_len == 0) { return -1; } /* * check the first character at right of the dot */ if (is_ip == 0) { if (!light_isalpha(host->ptr[i + 1])) { return -1; } } else if (!light_isdigit(host->ptr[i + 1])) { is_ip = 0; } else if ('-' == host->ptr[i + 1]) { return -1; } else { /* * just digits */ is_ip = 1; } stage = DOMAINLABEL; label_len = 0; level++; } else if (i == 0) { /* * just a dot and nothing else is evil */ return -1; } } else if (i == 0)
int config_read(server *srv, const char *fn) { config_t context; data_config *dc; data_integer *dpid; data_string *dcwd; int ret; char *pos; data_array *modules; context_init(srv, &context); context.all_configs = srv->config_context; #ifdef __WIN32 pos = strrchr(fn, '\\'); #else pos = strrchr(fn, '/'); #endif if (pos) { buffer_copy_string_len(context.basedir, fn, pos - fn + 1); fn = pos + 1; } dc = data_config_init(); buffer_copy_string_len(dc->key, CONST_STR_LEN("global")); assert(context.all_configs->used == 0); dc->context_ndx = context.all_configs->used; array_insert_unique(context.all_configs, (data_unset *)dc); context.current = dc; /* default context */ srv->config = dc->value; dpid = data_integer_init(); dpid->value = getpid(); buffer_copy_string_len(dpid->key, CONST_STR_LEN("var.PID")); array_insert_unique(srv->config, (data_unset *)dpid); dcwd = data_string_init(); buffer_prepare_copy(dcwd->value, 1024); if (NULL != getcwd(dcwd->value->ptr, dcwd->value->size - 1)) { dcwd->value->used = strlen(dcwd->value->ptr) + 1; buffer_copy_string_len(dcwd->key, CONST_STR_LEN("var.CWD")); array_insert_unique(srv->config, (data_unset *)dcwd); } ret = config_parse_file(srv, &context, fn); /* remains nothing if parser is ok */ assert(!(0 == ret && context.ok && 0 != context.configs_stack->used)); context_free(&context); if (0 != ret) { return ret; } if (NULL != (dc = (data_config *)array_get_element(srv->config_context, "global"))) { srv->config = dc->value; } else { return -1; } if (NULL != (modules = (data_array *)array_get_element(srv->config, "server.modules"))) { data_string *ds; data_array *prepends; if (modules->type != TYPE_ARRAY) { fprintf(stderr, "server.modules must be an array"); return -1; } prepends = data_array_init(); /* prepend default modules */ if (NULL == array_get_element(modules->value, "mod_indexfile")) { ds = data_string_init(); buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_indexfile")); array_insert_unique(prepends->value, (data_unset *)ds); } prepends = (data_array *)configparser_merge_data((data_unset *)prepends, (data_unset *)modules); buffer_copy_string_buffer(prepends->key, modules->key); array_replace(srv->config, (data_unset *)prepends); modules->free((data_unset *)modules); modules = prepends; /* append default modules */ if (NULL == array_get_element(modules->value, "mod_dirlisting")) { ds = data_string_init(); buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_dirlisting")); array_insert_unique(modules->value, (data_unset *)ds); } if (NULL == array_get_element(modules->value, "mod_staticfile")) { ds = data_string_init(); buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_staticfile")); array_insert_unique(modules->value, (data_unset *)ds); } } else { data_string *ds; modules = data_array_init(); /* server.modules is not set */ ds = data_string_init(); buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_indexfile")); array_insert_unique(modules->value, (data_unset *)ds); ds = data_string_init(); buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_dirlisting")); array_insert_unique(modules->value, (data_unset *)ds); ds = data_string_init(); buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_staticfile")); array_insert_unique(modules->value, (data_unset *)ds); buffer_copy_string_len(modules->key, CONST_STR_LEN("server.modules")); array_insert_unique(srv->config, (data_unset *)modules); } if (0 != config_insert(srv)) { return -1; } return 0; }
handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s) { #ifdef USE_LDAP int ret; #if 0 if (s->auth_ldap_basedn->used == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.base-dn has to be set"); return HANDLER_ERROR; } #endif if (s->auth_ldap_filter->used) { char *dollar; /* parse filter */ if (NULL == (dollar = strchr(s->auth_ldap_filter->ptr, '$'))) { log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '$'"); return HANDLER_ERROR; } buffer_copy_string_len(s->ldap_filter_pre, s->auth_ldap_filter->ptr, dollar - s->auth_ldap_filter->ptr); buffer_copy_string(s->ldap_filter_post, dollar+1); } if (s->auth_ldap_hostname->used) { if (NULL == (s->ldap = ldap_init(s->auth_ldap_hostname->ptr, LDAP_PORT))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap ...", strerror(errno)); return HANDLER_ERROR; } ret = LDAP_VERSION3; if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(s->ldap, LDAP_OPT_PROTOCOL_VERSION, &ret))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); return HANDLER_ERROR; } if (s->auth_ldap_starttls) { /* if no CA file is given, it is ok, as we will use encryption * if the server requires a CAfile it will tell us */ if (!buffer_is_empty(s->auth_ldap_cafile)) { if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, s->auth_ldap_cafile->ptr))) { log_error_write(srv, __FILE__, __LINE__, "ss", "Loading CA certificate failed:", ldap_err2string(ret)); return HANDLER_ERROR; } } if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(s->ldap, NULL, NULL))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret)); return HANDLER_ERROR; } } /* 1. */ if (s->auth_ldap_binddn->used) { if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, s->auth_ldap_binddn->ptr, s->auth_ldap_bindpw->ptr))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); return HANDLER_ERROR; } } else { if (LDAP_SUCCESS != (ret = ldap_simple_bind_s(s->ldap, NULL, NULL))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); return HANDLER_ERROR; } } } #else log_error_write(srv, __FILE__, __LINE__, "s", "no ldap support available"); return HANDLER_ERROR; #endif return HANDLER_GO_ON; }
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; }
int connection_state_machine(server *srv, connection *con) { int done = 0, r; if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at start", con->fd, connection_get_state(con->state)); } while (done == 0) { size_t ostate = con->state; if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } switch (con->state) { case CON_STATE_REQUEST_START: /* transient */ con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; if (con->conf.high_precision_timestamps) log_clock_gettime_realtime(&con->request_start_hp); con->request_count++; con->loops_per_request = 0; connection_set_state(srv, con, CON_STATE_READ); break; case CON_STATE_REQUEST_END: /* transient */ buffer_reset(con->uri.authority); buffer_reset(con->uri.path); buffer_reset(con->uri.query); buffer_reset(con->request.orig_uri); if (http_request_parse(srv, con)) { /* we have to read some data from the POST request */ connection_set_state(srv, con, CON_STATE_READ_POST); break; } connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case CON_STATE_READ_POST: case CON_STATE_HANDLE_REQUEST: /* * the request is parsed * * decided what to do with the request * - * * */ switch (r = http_response_prepare(srv, con)) { case HANDLER_WAIT_FOR_EVENT: if (!con->file_finished && (!con->file_started || 0 == con->conf.stream_response_body)) { break; /* come back here */ } /* response headers received from backend; fall through to start response */ case HANDLER_FINISHED: if (con->error_handler_saved_status > 0) { con->request.http_method = con->error_handler_saved_method; } if (con->mode == DIRECT) { if (con->error_handler_saved_status) { if (con->error_handler_saved_status > 0) { con->http_status = con->error_handler_saved_status; } else if (con->http_status == 404 || con->http_status == 403) { /* error-handler-404 is a 404 */ con->http_status = -con->error_handler_saved_status; } else { /* error-handler-404 is back and has generated content */ /* if Status: was set, take it otherwise use 200 */ } } else if (con->http_status >= 400) { buffer *error_handler = NULL; if (!buffer_string_is_empty(con->conf.error_handler)) { error_handler = con->conf.error_handler; } else if ((con->http_status == 404 || con->http_status == 403) && !buffer_string_is_empty(con->conf.error_handler_404)) { error_handler = con->conf.error_handler_404; } if (error_handler) { /* call error-handler */ /* set REDIRECT_STATUS to save current HTTP status code * for access by dynamic handlers * https://redmine.lighttpd.net/issues/1828 */ data_string *ds; if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string_len(ds->key, CONST_STR_LEN("REDIRECT_STATUS")); buffer_append_int(ds->value, con->http_status); array_insert_unique(con->environment, (data_unset *)ds); if (error_handler == con->conf.error_handler) { plugins_call_connection_reset(srv, con); if (con->request.content_length) { if (con->request.content_length != con->request_content_queue->bytes_in) { con->keep_alive = 0; } con->request.content_length = 0; chunkqueue_reset(con->request_content_queue); } con->is_writable = 1; con->file_finished = 0; con->file_started = 0; con->got_response = 0; con->parsed_response = 0; con->response.keep_alive = 0; con->response.content_length = -1; con->response.transfer_encoding = 0; con->error_handler_saved_status = con->http_status; con->error_handler_saved_method = con->request.http_method; con->request.http_method = HTTP_METHOD_GET; } else { /*(preserve behavior for server.error-handler-404)*/ con->error_handler_saved_status = -con->http_status; /*(negative to flag old behavior)*/ } buffer_copy_buffer(con->request.uri, error_handler); connection_handle_errdoc_init(srv, con); con->http_status = 0; /*(after connection_handle_errdoc_init())*/ done = -1; break; } } } if (con->http_status == 0) con->http_status = 200; /* we have something to send, go on */ connection_set_state(srv, con, CON_STATE_RESPONSE_START); break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); break; case HANDLER_COMEBACK: done = -1; break; case HANDLER_ERROR: /* something went wrong */ connection_set_state(srv, con, CON_STATE_ERROR); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); break; } if (con->state == CON_STATE_HANDLE_REQUEST && ostate == CON_STATE_READ_POST) { ostate = CON_STATE_HANDLE_REQUEST; } break; case CON_STATE_RESPONSE_START: /* * the decision is done * - create the HTTP-Response-Header * */ if (-1 == connection_handle_write_prepare(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); break; } connection_set_state(srv, con, CON_STATE_WRITE); break; case CON_STATE_RESPONSE_END: /* transient */ case CON_STATE_ERROR: /* transient */ connection_handle_response_end_state(srv, con); break; case CON_STATE_CONNECT: chunkqueue_reset(con->read_queue); con->request_count = 0; break; case CON_STATE_CLOSE: connection_handle_close_state(srv, con); break; case CON_STATE_READ: connection_handle_read_state(srv, con); break; case CON_STATE_WRITE: do { /* only try to write if we have something in the queue */ if (!chunkqueue_is_empty(con->write_queue)) { if (con->is_writable) { if (-1 == connection_handle_write(srv, con)) { log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); connection_set_state(srv, con, CON_STATE_ERROR); break; } if (con->state != CON_STATE_WRITE) break; } } else if (con->file_finished) { connection_set_state(srv, con, CON_STATE_RESPONSE_END); break; } if (con->mode != DIRECT && !con->file_finished) { switch(r = plugins_call_handle_subrequest(srv, con)) { case HANDLER_WAIT_FOR_EVENT: case HANDLER_FINISHED: case HANDLER_GO_ON: break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); break; case HANDLER_COMEBACK: default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unexpected subrequest handler ret-value: ", con->fd, r); /* fall through */ case HANDLER_ERROR: connection_set_state(srv, con, CON_STATE_ERROR); break; } } } while (con->state == CON_STATE_WRITE && (!chunkqueue_is_empty(con->write_queue) ? con->is_writable : con->file_finished)); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown state:", con->fd, con->state); break; } if (done == -1) { done = 0; } else if (ostate == con->state) { done = 1; } } if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at exit:", con->fd, connection_get_state(con->state)); } r = 0; switch(con->state) { case CON_STATE_READ: case CON_STATE_CLOSE: r = FDEVENT_IN; break; case CON_STATE_WRITE: /* request write-fdevent only if we really need it * - if we have data to write * - if the socket is not writable yet */ if (!chunkqueue_is_empty(con->write_queue) && (con->is_writable == 0) && (con->traffic_limit_reached == 0)) { r |= FDEVENT_OUT; } /* fall through */ case CON_STATE_READ_POST: if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN) { r |= FDEVENT_IN; } break; default: break; } if (-1 != con->fd) { const int events = fdevent_event_get_interest(srv->ev, con->fd); if (con->is_readable < 0) { con->is_readable = 0; r |= FDEVENT_IN; } if (con->is_writable < 0) { con->is_writable = 0; r |= FDEVENT_OUT; } if (r != events) { /* update timestamps when enabling interest in events */ if ((r & FDEVENT_IN) && !(events & FDEVENT_IN)) { con->read_idle_ts = srv->cur_ts; } if ((r & FDEVENT_OUT) && !(events & FDEVENT_OUT)) { con->write_request_ts = srv->cur_ts; } fdevent_event_set(srv->ev, &con->fde_ndx, con->fd, r); } } return 0; }
/* * copy/steal max_len bytes from chunk chain. return total bytes copied/stolen. * */ off_t chunkqueue_steal_chunks_len(chunkqueue *out, chunk *c, off_t max_len) { off_t total = 0; off_t we_have = 0, we_want = 0; buffer *b; if (!out || !c) return 0; /* copy/steal chunks */ for (; c && max_len > 0; c = c->next) { switch (c->type) { case FILE_CHUNK: we_have = c->file.length - c->offset; if (we_have == 0) break; if (we_have > max_len) we_have = max_len; chunkqueue_append_file(out, c->file.name, c->offset, we_have); c->offset += we_have; max_len -= we_have; total += we_have; /* steal the tempfile * * This is tricky: * - we reference the tempfile from the in-queue several times * if the chunk is larger than max_len * - we can't simply cleanup the in-queue as soon as possible * as it would remove the tempfiles * - the idea is to 'steal' the tempfiles and attach the is_temp flag to the last * referencing chunk of the fastcgi-write-queue * */ if (c->offset == c->file.length) { chunk *out_c; out_c = out->last; /* the last of the out-queue should be a FILE_CHUNK (we just created it) * and the incoming side should have given use a temp-file-chunk */ assert(out_c->type == FILE_CHUNK); assert(c->file.is_temp == 1); out_c->file.is_temp = 1; c->file.is_temp = 0; } break; case MEM_CHUNK: /* skip empty chunks */ if (c->mem->used == 0) break; we_have = c->mem->used - c->offset - 1; if (we_have == 0) break; we_want = we_have < max_len ? we_have : max_len; if (we_have == we_want) { /* steal whole chunk */ chunkqueue_steal_chunk(out, c); } else { /* copy unused data from chunk */ b = chunkqueue_get_append_buffer(out); buffer_copy_string_len(b, c->mem->ptr + c->offset, we_want); c->offset += we_want; } total += we_want; max_len -= we_want; break; default: break; } } return total; }
static int mod_rrdtool_create_rrd(server *srv, plugin_data *p, plugin_config *s) { struct stat st; int r; /* check if DB already exists */ if (0 == stat(s->path_rrd->ptr, &st)) { /* check if it is plain file */ if (!S_ISREG(st.st_mode)) { log_error_write(srv, __FILE__, __LINE__, "sb", "not a regular file:", s->path_rrd); return HANDLER_ERROR; } /* still create DB if it's empty file */ if (st.st_size > 0) { return HANDLER_GO_ON; } } /* create a new one */ buffer_copy_string_len(p->cmd, CONST_STR_LEN("create ")); buffer_append_string_buffer(p->cmd, s->path_rrd); buffer_append_string_len(p->cmd, CONST_STR_LEN( " --step 60 " "DS:InOctets:ABSOLUTE:600:U:U " "DS:OutOctets:ABSOLUTE:600:U:U " "DS:Requests:ABSOLUTE:600:U:U " "RRA:AVERAGE:0.5:1:600 " "RRA:AVERAGE:0.5:6:700 " "RRA:AVERAGE:0.5:24:775 " "RRA:AVERAGE:0.5:288:797 " "RRA:MAX:0.5:1:600 " "RRA:MAX:0.5:6:700 " "RRA:MAX:0.5:24:775 " "RRA:MAX:0.5:288:797 " "RRA:MIN:0.5:1:600 " "RRA:MIN:0.5:6:700 " "RRA:MIN:0.5:24:775 " "RRA:MIN:0.5:288:797\n")); if (-1 == (safe_write(p->write_fd, p->cmd->ptr, p->cmd->used - 1))) { log_error_write(srv, __FILE__, __LINE__, "ss", "rrdtool-write: failed", strerror(errno)); return HANDLER_ERROR; } buffer_prepare_copy(p->resp, 4096); if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size))) { log_error_write(srv, __FILE__, __LINE__, "ss", "rrdtool-read: failed", strerror(errno)); return HANDLER_ERROR; } p->resp->used = r; if (p->resp->ptr[0] != 'O' || p->resp->ptr[1] != 'K') { log_error_write(srv, __FILE__, __LINE__, "sbb", "rrdtool-response:", p->cmd, p->resp); return HANDLER_ERROR; } return HANDLER_GO_ON; }
static int ssi_expr_tokenizer(server *srv, connection *con, handler_ctx *p, ssi_tokenizer_t *t, int *token_id, buffer *token) { int tid = 0; size_t i; UNUSED(con); for (tid = 0; tid == 0 && t->offset < t->size && t->input[t->offset] ; ) { char c = t->input[t->offset]; data_string *ds; switch (c) { case '=': tid = TK_EQ; t->offset++; t->line_pos++; buffer_copy_string_len(token, CONST_STR_LEN("(=)")); break; case '>': if (t->input[t->offset + 1] == '=') { t->offset += 2; t->line_pos += 2; tid = TK_GE; buffer_copy_string_len(token, CONST_STR_LEN("(>=)")); } else { t->offset += 1; t->line_pos += 1; tid = TK_GT; buffer_copy_string_len(token, CONST_STR_LEN("(>)")); } break; case '<': if (t->input[t->offset + 1] == '=') { t->offset += 2; t->line_pos += 2; tid = TK_LE; buffer_copy_string_len(token, CONST_STR_LEN("(<=)")); } else { t->offset += 1; t->line_pos += 1; tid = TK_LT; buffer_copy_string_len(token, CONST_STR_LEN("(<)")); } break; case '!': if (t->input[t->offset + 1] == '=') { t->offset += 2; t->line_pos += 2; tid = TK_NE; buffer_copy_string_len(token, CONST_STR_LEN("(!=)")); } else { t->offset += 1; t->line_pos += 1; tid = TK_NOT; buffer_copy_string_len(token, CONST_STR_LEN("(!)")); } break; case '&': if (t->input[t->offset + 1] == '&') { t->offset += 2; t->line_pos += 2; tid = TK_AND; buffer_copy_string_len(token, CONST_STR_LEN("(&&)")); } else { log_error_write(srv, __FILE__, __LINE__, "sds", "pos:", t->line_pos, "missing second &"); return -1; } break; case '|': if (t->input[t->offset + 1] == '|') { t->offset += 2; t->line_pos += 2; tid = TK_OR; buffer_copy_string_len(token, CONST_STR_LEN("(||)")); } else { log_error_write(srv, __FILE__, __LINE__, "sds", "pos:", t->line_pos, "missing second |"); return -1; } break; case '\t': case ' ': t->offset++; t->line_pos++; break; case '\'': /* search for the terminating " */ for (i = 1; t->input[t->offset + i] && t->input[t->offset + i] != '\''; i++); if (t->input[t->offset + i]) { tid = TK_VALUE; buffer_copy_string_len(token, t->input + t->offset + 1, i-1); t->offset += i + 1; t->line_pos += i + 1; } else { /* ERROR */ log_error_write(srv, __FILE__, __LINE__, "sds", "pos:", t->line_pos, "missing closing quote"); return -1; } break; case '(': t->offset++; t->in_brace++; tid = TK_LPARAN; buffer_copy_string_len(token, CONST_STR_LEN("(")); break; case ')': t->offset++; t->in_brace--; tid = TK_RPARAN; buffer_copy_string_len(token, CONST_STR_LEN(")")); break; case '$': if (t->input[t->offset + 1] == '{') { for (i = 2; t->input[t->offset + i] && t->input[t->offset + i] != '}'; i++); if (t->input[t->offset + i] != '}') { log_error_write(srv, __FILE__, __LINE__, "sds", "pos:", t->line_pos, "missing closing quote"); return -1; } buffer_copy_string_len(token, t->input + t->offset + 2, i-3); } else { for (i = 1; isalpha(t->input[t->offset + i]) || t->input[t->offset + i] == '_' || ((i > 1) && isdigit(t->input[t->offset + i])); i++); buffer_copy_string_len(token, t->input + t->offset + 1, i-1); } tid = TK_VALUE; if (NULL != (ds = (data_string *)array_get_element_klen(p->ssi_cgi_env, CONST_BUF_LEN(token)))) { buffer_copy_buffer(token, ds->value); } else if (NULL != (ds = (data_string *)array_get_element_klen(p->ssi_vars, CONST_BUF_LEN(token)))) { buffer_copy_buffer(token, ds->value); } else { buffer_copy_string_len(token, CONST_STR_LEN("")); } t->offset += i; t->line_pos += i; break; default: for (i = 0; isgraph(t->input[t->offset + i]); i++) { char d = t->input[t->offset + i]; switch(d) { case ' ': case '\t': case ')': case '(': case '\'': case '=': case '!': case '<': case '>': case '&': case '|': break; } } tid = TK_VALUE; buffer_copy_string_len(token, t->input + t->offset, i); t->offset += i; t->line_pos += i; break; } } if (tid) { *token_id = tid; return 1; } else if (t->offset < t->size) { log_error_write(srv, __FILE__, __LINE__, "sds", "pos:", t->line_pos, "foobar"); } return 0; }
/** * 输出日志. * 日志的格式: * 2009-11-25 22:31:25: (filename.line) information * * 参数fmt的说明如下: * 's':字符串 'b':buffer 'd':int 'o':off_t 'x':int的十六进制 * 上面的几个参数,在输出相应的值后都追加一个空格' '。 * 如果参数为大写,则不追加空格。 * */ int log_error_write(server * srv, const char *filename, unsigned int line, const char *fmt, ...) { pthread_mutex_lock(&srv -> log_lock); va_list ap; switch (srv->errorlog_mode) { case ERRORLOG_FILE: case ERRORLOG_STDERR: /* * 日志文件和标准错误输出要设定日志的时间。 */ if (srv->cur_ts != srv->last_generated_debug_ts) { buffer_prepare_copy(srv->ts_debug_str, 1024); strftime(srv->ts_debug_str->ptr, srv->ts_debug_str->size - 1, "%Y-%m-%d %H:%M:%S", localtime(&(srv->cur_ts))); srv->ts_debug_str->used = strlen(srv->ts_debug_str->ptr) + 1; srv->last_generated_debug_ts = srv->cur_ts; } buffer_copy_string_buffer(srv->errorlog_buf, srv->ts_debug_str); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(": (")); break; case ERRORLOG_SYSLOG: /* * syslog自己产生时间 */ buffer_copy_string_len(srv->errorlog_buf, CONST_STR_LEN("(")); break; } buffer_append_string(srv->errorlog_buf, filename); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(".")); buffer_append_long(srv->errorlog_buf, line); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(") ")); //根据字符串fmt来遍历可变参数。 for (va_start(ap, fmt); *fmt; fmt++) { int d; char *s; buffer *b; off_t o; switch (*fmt) { case 's': /* string */ s = va_arg(ap, char *); buffer_append_string(srv->errorlog_buf, s); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'b': /* buffer */ b = va_arg(ap, buffer *); buffer_append_string_buffer(srv->errorlog_buf, b); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'd': /* int */ d = va_arg(ap, int); buffer_append_long(srv->errorlog_buf, d); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'o': /* off_t */ o = va_arg(ap, off_t); buffer_append_off_t(srv->errorlog_buf, o); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'x': /* int (hex) */ d = va_arg(ap, int); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("0x")); buffer_append_long_hex(srv->errorlog_buf, d); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN(" ")); break; case 'S': /* string */ s = va_arg(ap, char *); buffer_append_string(srv->errorlog_buf, s); break; case 'B': /* buffer */ b = va_arg(ap, buffer *); buffer_append_string_buffer(srv->errorlog_buf, b); break; case 'D': /* int */ d = va_arg(ap, int); buffer_append_long(srv->errorlog_buf, d); break; case 'O': /* off_t */ o = va_arg(ap, off_t); buffer_append_off_t(srv->errorlog_buf, o); break; case 'X': /* int (hex) */ d = va_arg(ap, int); buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("0x")); buffer_append_long_hex(srv->errorlog_buf, d); break; case '(': case ')': case '<': case '>': case ',': case ' ': buffer_append_string_len(srv->errorlog_buf, fmt, 1); break; } } va_end(ap); switch (srv->errorlog_mode) { case ERRORLOG_FILE: buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("\n")); write(srv->errorlog_fd, srv->errorlog_buf->ptr, srv->errorlog_buf->used - 1); break; case ERRORLOG_STDERR: buffer_append_string_len(srv->errorlog_buf, CONST_STR_LEN("\n")); write(STDERR_FILENO, srv->errorlog_buf->ptr, srv->errorlog_buf->used - 1); break; case ERRORLOG_SYSLOG: syslog(LOG_ERR, "%s", srv->errorlog_buf->ptr); break; } pthread_mutex_unlock(&srv -> log_lock); 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)) { close(to_cgi_fds[0]); close(to_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 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; if (buffer_is_empty(con->conf.server_tag)) { cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC)); } else { cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag)); } if (!buffer_is_empty(con->server_name)) { size_t len = con->server_name->used - 1; if (con->server_name->ptr[0] == '[') { const char *colon = strstr(con->server_name->ptr, "]:"); if (colon) len = (colon + 1) - con->server_name->ptr; } else { const 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 { #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)); LI_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)); switch (srv_sock->addr.plain.sa_family) { #ifdef HAVE_IPV6 case AF_INET6: s = inet_ntop(srv_sock->addr.plain.sa_family, (const void *) &(srv_sock->addr.ipv6.sin6_addr), b2, sizeof(b2)-1); break; case AF_INET: s = inet_ntop(srv_sock->addr.plain.sa_family, (const void *) &(srv_sock->addr.ipv4.sin_addr), b2, sizeof(b2)-1); break; #else case AF_INET: s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); break; #endif default: s = ""; break; } 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)); } switch (con->dst_addr.plain.sa_family) { #ifdef HAVE_IPV6 case AF_INET6: s = inet_ntop(con->dst_addr.plain.sa_family, (const void *) &(con->dst_addr.ipv6.sin6_addr), b2, sizeof(b2)-1); break; case AF_INET: s = inet_ntop(con->dst_addr.plain.sa_family, (const void *) &(con->dst_addr.ipv4.sin_addr), b2, sizeof(b2)-1); break; #else case AF_INET: s = inet_ntoa(con->dst_addr.ipv4.sin_addr); break; #endif default: s = ""; break; } cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); LI_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)); } if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) { cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); } /* request.content_length < SSIZE_MAX, see request.c */ 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.basedir)); /* 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->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)); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(to_cgi_fds[0]); close(to_cgi_fds[1]); return -1; 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(NULL, 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; case EINTR: continue; 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; case EINTR: continue; default: con->http_status = 403; break; } } break; case UNUSED_CHUNK: break; } if (r > 0) { c->offset += r; cq->bytes_out += r; } else { log_error_write(srv, __FILE__, __LINE__, "ss", "write() failed due to: ", strerror(errno)); con->http_status = 500; 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_set(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 }
int network_write_chunkqueue_writev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { chunk *c; for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { int chunk_finished = 0; switch(c->type) { case MEM_CHUNK: { char * offset; off_t toSend; ssize_t r; size_t num_chunks, i; struct iovec *chunks; chunk *tc; size_t num_bytes = 0; /* build writev list * * 1. limit: num_chunks < MAX_CHUNKS * 2. limit: num_bytes < max_bytes */ for (num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < MAX_CHUNKS; num_chunks++, tc = tc->next); chunks = calloc(num_chunks, sizeof(*chunks)); for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) { if (buffer_string_is_empty(tc->mem)) { chunks[i].iov_base = tc->mem->ptr; chunks[i].iov_len = 0; } else { offset = tc->mem->ptr + tc->offset; toSend = buffer_string_length(tc->mem) - tc->offset; chunks[i].iov_base = offset; /* protect the return value of writev() */ if (toSend > max_bytes || (off_t) num_bytes + toSend > max_bytes) { chunks[i].iov_len = max_bytes - num_bytes; num_chunks = i + 1; break; } else { chunks[i].iov_len = toSend; } num_bytes += toSend; } } if ((r = writev(fd, chunks, num_chunks)) < 0) { switch (errno) { case EAGAIN: case EINTR: r = 0; break; case EPIPE: case ECONNRESET: free(chunks); return -2; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "writev failed:", strerror(errno), fd); free(chunks); return -1; } } cq->bytes_out += r; max_bytes -= r; /* check which chunks have been written */ for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { if (r >= (ssize_t)chunks[i].iov_len) { /* written */ r -= chunks[i].iov_len; tc->offset += chunks[i].iov_len; if (chunk_finished) { /* skip the chunks from further touches */ c = c->next; } else { /* chunks_written + c = c->next is done in the for()*/ chunk_finished = 1; } } else { /* partially written */ tc->offset += r; chunk_finished = 0; break; } } free(chunks); break; } case FILE_CHUNK: { ssize_t r; off_t abs_offset; off_t toSend; stat_cache_entry *sce = NULL; #define KByte * 1024 #define MByte * 1024 KByte #define GByte * 1024 MByte const off_t we_want_to_mmap = 512 KByte; char *start = NULL; if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), c->file.name); return -1; } abs_offset = c->file.start + c->offset; if (abs_offset > sce->st.st_size) { log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); return -1; } /* mmap the buffer * - first mmap * - new mmap as the we are at the end of the last one */ if (c->file.mmap.start == MAP_FAILED || abs_offset == (off_t)(c->file.mmap.offset + c->file.mmap.length)) { /* Optimizations for the future: * * adaptive mem-mapping * the problem: * we mmap() the whole file. If someone has alot large files and 32bit * machine the virtual address area will be unrun and we will have a failing * mmap() call. * solution: * only mmap 16M in one chunk and move the window as soon as we have finished * the first 8M * * read-ahead buffering * the problem: * sending out several large files in parallel trashes the read-ahead of the * kernel leading to long wait-for-seek times. * solutions: (increasing complexity) * 1. use madvise * 2. use a internal read-ahead buffer in the chunk-structure * 3. use non-blocking IO for file-transfers * */ /* all mmap()ed areas are 512kb expect the last which might be smaller */ off_t we_want_to_send; size_t to_mmap; /* this is a remap, move the mmap-offset */ if (c->file.mmap.start != MAP_FAILED) { munmap(c->file.mmap.start, c->file.mmap.length); c->file.mmap.offset += we_want_to_mmap; } else { /* in case the range-offset is after the first mmap()ed area we skip the area */ c->file.mmap.offset = 0; while (c->file.mmap.offset + we_want_to_mmap < c->file.start) { c->file.mmap.offset += we_want_to_mmap; } } /* length is rel, c->offset too, assume there is no limit at the mmap-boundaries */ we_want_to_send = c->file.length - c->offset; to_mmap = (c->file.start + c->file.length) - c->file.mmap.offset; /* we have more to send than we can mmap() at once */ if (abs_offset + we_want_to_send > c->file.mmap.offset + we_want_to_mmap) { we_want_to_send = (c->file.mmap.offset + we_want_to_mmap) - abs_offset; to_mmap = we_want_to_mmap; } if (-1 == c->file.fd) { /* open the file if not already open */ if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "open failed for:", c->file.name, strerror(errno)); return -1; } fd_close_on_exec(c->file.fd); } if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) { log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed:", strerror(errno), c->file.name, c->file.fd); return -1; } c->file.mmap.length = to_mmap; #ifdef LOCAL_BUFFERING buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length); #else #ifdef HAVE_MADVISE /* don't advise files < 64Kb */ if (c->file.mmap.length > (64 KByte)) { /* darwin 7 is returning EINVAL all the time and I don't know how to * detect this at runtime.i * * ignore the return value for now */ madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED); } #endif #endif /* chunk_reset() or chunk_free() will cleanup for us */ } /* to_send = abs_mmap_end - abs_offset */ toSend = (c->file.mmap.offset + c->file.mmap.length) - (abs_offset); if (toSend < 0) { log_error_write(srv, __FILE__, __LINE__, "soooo", "toSend is negative:", toSend, c->file.mmap.length, abs_offset, c->file.mmap.offset); force_assert(toSend < 0); } if (toSend > max_bytes) toSend = max_bytes; #ifdef LOCAL_BUFFERING start = c->mem->ptr; #else start = c->file.mmap.start; #endif if ((r = write(fd, start + (abs_offset - c->file.mmap.offset), toSend)) < 0) { switch (errno) { case EAGAIN: case EINTR: r = 0; break; case EPIPE: case ECONNRESET: return -2; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed:", strerror(errno), fd); return -1; } } c->offset += r; cq->bytes_out += r; max_bytes -= r; if (c->offset == c->file.length) { chunk_finished = 1; /* we don't need the mmaping anymore */ if (c->file.mmap.start != MAP_FAILED) { munmap(c->file.mmap.start, c->file.mmap.length); c->file.mmap.start = MAP_FAILED; } } break; } default: log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); return -1; } if (!chunk_finished) { /* not finished yet */ break; } } return 0; }
static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *token) { int tid = 0; size_t i; for (tid = 0; tid == 0 && t->offset < t->size && t->input[t->offset] ; ) { char c = t->input[t->offset]; const char *start = NULL; switch (c) { case '=': if (t->in_brace) { if (t->input[t->offset + 1] == '>') { t->offset += 2; buffer_copy_string_len(token, CONST_STR_LEN("=>")); tid = TK_ARRAY_ASSIGN; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, "line:", t->line, "pos:", t->line_pos, "use => for assignments in arrays"); return -1; } } else if (t->in_cond) { if (t->input[t->offset + 1] == '=') { t->offset += 2; buffer_copy_string_len(token, CONST_STR_LEN("==")); tid = TK_EQ; } else if (t->input[t->offset + 1] == '~') { t->offset += 2; buffer_copy_string_len(token, CONST_STR_LEN("=~")); tid = TK_MATCH; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, "line:", t->line, "pos:", t->line_pos, "only =~ and == are allowed in the condition"); return -1; } t->in_key = 1; t->in_cond = 0; } else if (t->in_key) { tid = TK_ASSIGN; buffer_copy_string_len(token, t->input + t->offset, 1); t->offset++; t->line_pos++; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, "line:", t->line, "pos:", t->line_pos, "unexpected equal-sign: ="); return -1; } break; case '!': if (t->in_cond) { if (t->input[t->offset + 1] == '=') { t->offset += 2; buffer_copy_string_len(token, CONST_STR_LEN("!=")); tid = TK_NE; } else if (t->input[t->offset + 1] == '~') { t->offset += 2; buffer_copy_string_len(token, CONST_STR_LEN("!~")); tid = TK_NOMATCH; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, "line:", t->line, "pos:", t->line_pos, "only !~ and != are allowed in the condition"); return -1; } t->in_key = 1; t->in_cond = 0; } else { log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, "line:", t->line, "pos:", t->line_pos, "unexpected exclamation-marks: !"); return -1; } break; case '\t': case ' ': t->offset++; t->line_pos++; break; case '\n': case '\r': if (t->in_brace == 0) { int done = 0; while (!done && t->offset < t->size) { switch (t->input[t->offset]) { case '\r': case '\n': config_skip_newline(t); t->line_pos = 1; t->line++; break; case '#': t->line_pos += config_skip_comment(t); break; case '\t': case ' ': t->offset++; t->line_pos++; break; default: done = 1; } } t->in_key = 1; tid = TK_EOL; buffer_copy_string_len(token, CONST_STR_LEN("(EOL)")); } else { config_skip_newline(t); t->line_pos = 1; t->line++; } break; case ',': if (t->in_brace > 0) { tid = TK_COMMA; buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)")); } t->offset++; t->line_pos++; break; case '"': /* search for the terminating " */ start = t->input + t->offset + 1; buffer_copy_string_len(token, CONST_STR_LEN("")); for (i = 1; t->input[t->offset + i]; i++) { if (t->input[t->offset + i] == '\\' && t->input[t->offset + i + 1] == '"') { buffer_append_string_len(token, start, t->input + t->offset + i - start); start = t->input + t->offset + i + 1; /* skip the " */ i++; continue; } if (t->input[t->offset + i] == '"') { tid = TK_STRING; buffer_append_string_len(token, start, t->input + t->offset + i - start); break; } } if (t->input[t->offset + i] == '\0') { /* ERROR */ log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, "line:", t->line, "pos:", t->line_pos, "missing closing quote"); return -1; } t->offset += i + 1; t->line_pos += i + 1; break; case '(': t->offset++; t->in_brace++; tid = TK_LPARAN; buffer_copy_string_len(token, CONST_STR_LEN("(")); break; case ')': t->offset++; t->in_brace--; tid = TK_RPARAN; buffer_copy_string_len(token, CONST_STR_LEN(")")); break; case '$': t->offset++; tid = TK_DOLLAR; t->in_cond = 1; t->in_key = 0; buffer_copy_string_len(token, CONST_STR_LEN("$")); break; case '+': if (t->input[t->offset + 1] == '=') { t->offset += 2; buffer_copy_string_len(token, CONST_STR_LEN("+=")); tid = TK_APPEND; } else { t->offset++; tid = TK_PLUS; buffer_copy_string_len(token, CONST_STR_LEN("+")); } break; case '{': t->offset++; tid = TK_LCURLY; buffer_copy_string_len(token, CONST_STR_LEN("{")); break; case '}': t->offset++; tid = TK_RCURLY; buffer_copy_string_len(token, CONST_STR_LEN("}")); break; case '[': t->offset++; tid = TK_LBRACKET; buffer_copy_string_len(token, CONST_STR_LEN("[")); break; case ']': t->offset++; tid = TK_RBRACKET; buffer_copy_string_len(token, CONST_STR_LEN("]")); break; case '#': t->line_pos += config_skip_comment(t); break; default: if (t->in_cond) { for (i = 0; t->input[t->offset + i] && (isalpha((unsigned char)t->input[t->offset + i]) ); i++); if (i && t->input[t->offset + i]) { tid = TK_SRVVARNAME; buffer_copy_string_len(token, t->input + t->offset, i); t->offset += i; t->line_pos += i; } else { /* ERROR */ log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, "line:", t->line, "pos:", t->line_pos, "invalid character in condition"); return -1; } } else if (isdigit((unsigned char)c)) { /* take all digits */ for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]); i++); /* was there it least a digit ? */ if (i) { tid = TK_INTEGER; buffer_copy_string_len(token, t->input + t->offset, i); t->offset += i; t->line_pos += i; } } else { /* the key might consist of [-.0-9a-z] */ for (i = 0; t->input[t->offset + i] && (isalnum((unsigned char)t->input[t->offset + i]) || t->input[t->offset + i] == '.' || t->input[t->offset + i] == '_' || /* for env.* */ t->input[t->offset + i] == '-' ); i++); if (i && t->input[t->offset + i]) { buffer_copy_string_len(token, t->input + t->offset, i); if (strcmp(token->ptr, "include") == 0) { tid = TK_INCLUDE; } else if (strcmp(token->ptr, "include_shell") == 0) { tid = TK_INCLUDE_SHELL; } else if (strcmp(token->ptr, "global") == 0) { tid = TK_GLOBAL; } else if (strcmp(token->ptr, "else") == 0) { tid = TK_ELSE; } else { tid = TK_LKEY; } t->offset += i; t->line_pos += i; } else { /* ERROR */ log_error_write(srv, __FILE__, __LINE__, "sbsdsds", "source:", t->source, "line:", t->line, "pos:", t->line_pos, "invalid character in variable name"); return -1; } } break; } } if (tid) { *token_id = tid; #if 0 log_error_write(srv, __FILE__, __LINE__, "sbsdsdbdd", "source:", t->source, "line:", t->line, "pos:", t->line_pos, token, token->used - 1, tid); #endif return 1; } else if (t->offset < t->size) { fprintf(stderr, "%s.%d: %d, %s\n", __FILE__, __LINE__, tid, token->ptr); } return 0; }
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 handler_t proxy_http_parse_chunked_stream(server *srv, proxy_session *sess, chunkqueue *in, chunkqueue *out) { protocol_state_data *data = (protocol_state_data *)sess->proxy_con->protocol_data; char *err = NULL; off_t we_have = 0, we_want = 0; off_t chunk_len = 0; off_t offset = 0; buffer *b; chunk *c; char ch = '\0'; int finished = 0; UNUSED(srv); for (c = in->first; c && !finished;) { if(c->mem->used == 0) { c = c->next; continue; } switch(data->chunk_parse_state) { case HTTP_CHUNK_LEN: /* parse chunk len. */ for(offset = c->offset; (size_t)(offset) < (c->mem->used - 1) ; offset++) { ch = c->mem->ptr[offset]; if(!light_isxdigit(ch)) break; } if(offset > c->offset) { buffer_append_string_len(data->buf, (c->mem->ptr + c->offset), offset - c->offset); in->bytes_out += (offset - c->offset); c->offset = offset; } if (!(ch == ' ' || ch == '\r' || ch == ';')) { if (ch == '\0') { /* get next chunk from queue */ break; } /* protocol error. bad http-chunk len */ return HANDLER_ERROR; } data->chunk_len = strtol(BUF_STR(data->buf), &err, 16); data->chunk_offset = 0; buffer_reset(data->buf); data->chunk_parse_state = HTTP_CHUNK_EXTENSION; case HTTP_CHUNK_EXTENSION: /* find CRLF. discard chunk-extension */ for(ch = 0; (size_t)(c->offset) < (c->mem->used - 1) && ch != '\n' ;) { ch = c->mem->ptr[c->offset]; c->offset++; in->bytes_out++; } if(ch != '\n') { /* get next chunk from queue */ break; } if(data->chunk_len > 0) { data->chunk_parse_state = HTTP_CHUNK_DATA; } else { data->chunk_parse_state = HTTP_CHUNK_END; } case HTTP_CHUNK_DATA: chunk_len = data->chunk_len - data->chunk_offset; /* copy chunk_len bytes from in queue to out queue. */ we_have = c->mem->used - c->offset - 1; we_want = chunk_len > we_have ? we_have : chunk_len; if (c->offset == 0 && we_want == we_have) { /* we are copying the whole buffer, just steal it */ chunkqueue_steal_chunk(out, c); /* c is an empty chunk now */ } else { b = chunkqueue_get_append_buffer(out); buffer_copy_string_len(b, c->mem->ptr + c->offset, we_want); c->offset += we_want; } chunk_len -= we_want; out->bytes_in += we_want; in->bytes_out += we_want; data->chunk_offset += we_want; if(chunk_len > 0) { /* get next chunk from queue */ break; } data->chunk_offset = 0; data->chunk_parse_state = HTTP_CHUNK_END; case HTTP_CHUNK_END: /* discard CRLF.*/ for(ch = 0; c->mem->used > 0 && (size_t)(c->offset) < (c->mem->used - 1) && ch != '\n' ;) { ch = c->mem->ptr[c->offset]; c->offset++; in->bytes_out++; } if(ch != '\n') { /* get next chunk from queue */ break; } /* final chunk */ if(data->chunk_len == 0) { finished = 1; } /* finished http-chunk. reset and parse next chunk. */ protocol_state_data_reset(data); break; } if((size_t)(c->offset) == c->mem->used - 1) { c = c->next; } } chunkqueue_remove_finished_chunks(in); if (finished) { sess->is_request_finished = 1; return HANDLER_FINISHED; } /* ran out of data. */ return HANDLER_GO_ON; }
handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s) { #ifdef USE_LDAP int ret; struct berval credentials; char *binddn_ptr = NULL; if (s->auth_ldap_filter->used) { char *dollar; /* parse filter */ if (NULL == (dollar = strchr(s->auth_ldap_filter->ptr, '$'))) { log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '$'"); return HANDLER_ERROR; } buffer_copy_string_len(s->ldap->ldap_filter_pre, s->auth_ldap_filter->ptr, dollar - s->auth_ldap_filter->ptr); buffer_copy_string(s->ldap->ldap_filter_post, dollar+1); } if (s->auth_ldap_url->used) { if ((ret = ldap_initialize(&s->ldap->ldap, s->auth_ldap_url->ptr))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); return HANDLER_ERROR; } { const int ldap_version = LDAP_VERSION3; ret = ldap_set_option(s->ldap->ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); } if (ret != LDAP_OPT_SUCCESS) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); ldap_memfree(s->ldap->ldap); s->ldap->ldap = NULL; return HANDLER_ERROR; } if (s->auth_ldap_starttls == 1) { /* if no CA file is given, it is ok, as we will use encryption * if the server requires a CAfile it will tell us */ if (!buffer_is_empty(s->auth_ldap_cafile)) { if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, s->auth_ldap_cafile->ptr))) { log_error_write(srv, __FILE__, __LINE__, "ss", "Loading CA certificate failed:", ldap_err2string(ret)); ldap_memfree(s->ldap->ldap); s->ldap->ldap = NULL; return HANDLER_ERROR; } } if (!buffer_is_empty(s->auth_ldap_cert)) { if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, s->auth_ldap_cert->ptr))) { log_error_write(srv, __FILE__, __LINE__, "ss", "Loading TLS certificate failed:", ldap_err2string(ret)); ldap_memfree(s->ldap->ldap); s->ldap->ldap = NULL; return HANDLER_ERROR; } } if (!buffer_is_empty(s->auth_ldap_key)) { if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, s->auth_ldap_key->ptr))) { log_error_write(srv, __FILE__, __LINE__, "ss", "Loading TLS key certificate failed:", ldap_err2string(ret)); ldap_memfree(s->ldap->ldap); s->ldap->ldap = NULL; return HANDLER_ERROR; } } if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(s->ldap->ldap, NULL, NULL))) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret)); ldap_memfree(s->ldap->ldap); s->ldap->ldap = NULL; return HANDLER_ERROR; } } /* 1. */ if (s->auth_ldap_binddn->used) { credentials.bv_val = s->auth_ldap_bindpw->ptr; credentials.bv_len = s->auth_ldap_bindpw->used; binddn_ptr = s->auth_ldap_binddn->ptr; } else { credentials.bv_val = NULL; credentials.bv_len = 0; binddn_ptr = NULL; } ret = ldap_sasl_bind_s(s->ldap->ldap, s->auth_ldap_binddn->ptr, LDAP_SASL_SIMPLE, &credentials, NULL, NULL, NULL); if(ret != LDAP_SUCCESS) { log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); ldap_memfree(s->ldap->ldap); s->ldap->ldap = NULL; return HANDLER_ERROR; } } #else UNUSED(s); log_error_write(srv, __FILE__, __LINE__, "s", "no ldap support available"); return HANDLER_ERROR; #endif return HANDLER_GO_ON; }
void read_sharelink_list(){ #if EMBEDDED_EANBLE char* aa = nvram_get_sharelink_str(); if(aa==NULL){ return; } char* str_sharelink_list = (char*)malloc(strlen(aa)+1); strcpy(str_sharelink_list, aa); if(str_sharelink_list!=NULL){ char * pch; pch = strtok(str_sharelink_list, "<>"); while(pch!=NULL){ int b_addto_list = 1; share_link_info_t *smb_sharelink_info; smb_sharelink_info = (share_link_info_t *)calloc(1, sizeof(share_link_info_t)); smb_sharelink_info->toshare = 1; //- Share Path if(pch){ Cdbg(DBE, "share path=%s", pch); smb_sharelink_info->shortpath = buffer_init(); buffer_copy_string_len(smb_sharelink_info->shortpath, pch, strlen(pch)); } //- Real Path pch = strtok(NULL,"<>"); if(pch){ Cdbg(DBE, "real path=%s", pch); smb_sharelink_info->realpath = buffer_init(); buffer_copy_string_len(smb_sharelink_info->realpath, pch, strlen(pch)); } //- File Name pch = strtok(NULL,"<>"); if(pch){ Cdbg(DBE, "file name=%s", pch); smb_sharelink_info->filename = buffer_init(); buffer_copy_string_len(smb_sharelink_info->filename, pch, strlen(pch)); } //- Auth pch = strtok(NULL,"<>"); if(pch){ Cdbg(DBE, "auth=%s", pch); smb_sharelink_info->auth = buffer_init(); buffer_copy_string_len(smb_sharelink_info->auth, pch, strlen(pch)); } //- Expire Time pch = strtok(NULL,"<>"); if(pch){ smb_sharelink_info->expiretime = atoi(pch); time_t cur_time = time(NULL); double offset = difftime(smb_sharelink_info->expiretime, cur_time); if( smb_sharelink_info->expiretime !=0 && offset < 0.0 ){ free_share_link_info(smb_sharelink_info); free(smb_sharelink_info); b_addto_list = 0; } } //- Create Time pch = strtok(NULL,"<>"); if(pch){ smb_sharelink_info->createtime = atoi(pch); } if(b_addto_list==1) DLIST_ADD(share_link_info_list, smb_sharelink_info); //- Next pch = strtok(NULL,"<>"); } } #else size_t j; int length, filesize; FILE* fp = fopen(g_temp_sharelink_file, "r"); if(fp!=NULL){ char str[1024]; while(fgets(str,sizeof(str),fp) != NULL) { char * pch; int b_addto_list = 1; // strip trailing '\n' if it exists int len = strlen(str)-1; if(str[len] == '\n') str[len] = 0; share_link_info_t *smb_sharelink_info; smb_sharelink_info = (share_link_info_t *)calloc(1, sizeof(share_link_info_t)); smb_sharelink_info->toshare = 1; //- Share Path pch = strtok(str,"<"); if(pch){ smb_sharelink_info->shortpath = buffer_init(); buffer_copy_string_len(smb_sharelink_info->shortpath, pch, strlen(pch)); } //- Real Path pch = strtok(NULL,"<"); if(pch){ smb_sharelink_info->realpath = buffer_init(); buffer_copy_string_len(smb_sharelink_info->realpath, pch, strlen(pch)); } //- File Name pch = strtok(NULL,"<"); if(pch){ smb_sharelink_info->filename = buffer_init(); buffer_copy_string_len(smb_sharelink_info->filename, pch, strlen(pch)); } //- Auth pch = strtok(NULL,"<"); if(pch){ smb_sharelink_info->auth = buffer_init(); buffer_copy_string_len(smb_sharelink_info->auth, pch, strlen(pch)); } //- Expire Time pch = strtok(NULL,"<"); if(pch){ smb_sharelink_info->expiretime = atoi(pch); time_t cur_time = time(NULL); double offset = difftime(smb_sharelink_info->expiretime, cur_time); if( smb_sharelink_info->expiretime !=0 && offset < 0.0 ){ free_share_link_info(smb_sharelink_info); free(smb_sharelink_info); b_addto_list = 0; } } //- Create Time pch = strtok(NULL,"<"); if(pch){ smb_sharelink_info->createtime = atoi(pch); } if(b_addto_list==1) DLIST_ADD(share_link_info_list, smb_sharelink_info); } fclose(fp); } #endif }
/* * 处理所请求的资源是个目录。 */ static int response_redirect_to_directory(server *srv, connection *con) { if(NULL == srv || NULL == con) { return -1; } buffer *b = con -> tmp_buf; buffer_reset(b); buffer_copy_string_len(b, CONST_STR_LEN("http://")); if(NULL != con -> request.http_host) { buffer_append_string(b, con -> request.http_host); } else { //通过socket得到host名称。 struct hostent *he; sock_addr addr; socklen_t addr_len; addr_len = sizeof(addr); if(-1 == getsockname(con -> fd, &(addr.plain), &addr_len)) { con -> http_status = 500; log_error_write(srv, __FILE__, __LINE__, "s", "getsockname failed."); return 0; } switch(addr.plain.sa_family) { case AF_INET: /* * 只支持IPv4。 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * gethostbyaddr不是线程安全的! * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ if (NULL == (he = gethostbyaddr((char *)&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(b, inet_ntoa(addr.ipv4.sin_addr)); } else { buffer_append_string(b, he -> h_name); } break; default: log_error_write(srv, __FILE__, __LINE__, "s", "Unknown address type."); return -1; } } buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_string_buffer(b, con -> physical.real_path); if(!buffer_is_empty(con -> uri.query)) { buffer_append_string_len(b, CONST_STR_LEN("?")); buffer_append_string_buffer(b, con -> uri.query); } con -> http_status = 301; http_response_insert_header(srv, con, CONST_STR_LEN("Location") , b -> ptr, b -> used); return 0; }
static void https_add_ssl_entries(connection *con) { X509 *xs; X509_NAME *xn; X509_NAME_ENTRY *xe; int i, nentries; if ( SSL_get_verify_result(con->ssl) != X509_V_OK || !(xs = SSL_get_peer_certificate(con->ssl)) ) { return; } xn = X509_get_subject_name(xs); for (i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) { int xobjnid; const char * xobjsn; data_string *envds; if (!(xe = X509_NAME_get_entry(xn, i))) { continue; } xobjnid = OBJ_obj2nid((ASN1_OBJECT*)X509_NAME_ENTRY_get_object(xe)); xobjsn = OBJ_nid2sn(xobjnid); if (!xobjsn) { continue; } if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { envds = data_string_init(); } buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_S_DN_")); buffer_append_string(envds->key, xobjsn); buffer_copy_string_len( envds->value, (const char *)xe->value->data, xe->value->length ); /* pick one of the exported values as "authed user", for example * ssl.verifyclient.username = "******" or "SSL_CLIENT_S_DN_emailAddress" */ if (buffer_is_equal(con->conf.ssl_verifyclient_username, envds->key)) { buffer_copy_string_buffer(con->authed_user, envds->value); } array_insert_unique(con->environment, (data_unset *)envds); } if (con->conf.ssl_verifyclient_export_cert) { BIO *bio; if (NULL != (bio = BIO_new(BIO_s_mem()))) { data_string *envds; int n; PEM_write_bio_X509(bio, xs); n = BIO_pending(bio); if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { envds = data_string_init(); } buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_CERT")); buffer_prepare_copy(envds->value, n+1); BIO_read(bio, envds->value->ptr, n); BIO_free(bio); envds->value->ptr[n] = '\0'; envds->value->used = n+1; array_insert_unique(con->environment, (data_unset *)envds); } } X509_free(xs); }
handler_t http_prepare_response(server *srv, connection *con) { if (NULL == srv || NULL == con) { return HANDLER_ERROR; } if (con -> http_status >=400 && con -> http_status <600) { //有错误直接返回。 //说明已经处理过了。不需要在处理。 return HANDLER_FINISHED; } handler_t ht; /* * 分析uri地址。 /pages/index.html?key1=data&key2=data2#frangement * 将解析好的uri地址存放在con -> uri中。 * 其中fangement直接乎略。 */ buffer *uri = con -> request.uri; if (uri -> ptr[0] != '/') { log_error_write(srv, __FILE__, __LINE__, "sb", "bad uri. not begin with /.", uri); return HANDLER_ERROR; } //log_error_write(srv, __FILE__, __LINE__, "sb", "Request.uri", uri); char *query; if (NULL == (query = strchr(uri -> ptr, '?'))) { //没有query部分。只有uri地址。 buffer_copy_string(con -> uri.path_raw, uri -> ptr); } else { //有query数据。 char *frag; if (NULL != (frag =strchr(uri -> ptr, '#'))) { //有frangement数据。直接乎略。 *frag = '\0'; } *query = '\0'; ++query; buffer_copy_string(con -> uri.query, query); buffer_copy_string(con -> uri.path_raw, uri -> ptr); } //log_error_write(srv, __FILE__, __LINE__, "sb", "path_raw:", con -> uri.path_raw); //log_error_write(srv, __FILE__, __LINE__, "sb", "query:", con -> uri.query); buffer_copy_string_len(con -> uri.scheme, CONST_STR_LEN("http")); if (con->request.http_method == HTTP_METHOD_OPTIONS && con->uri.path->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { buffer_copy_string_len(con -> uri.path_raw, CONST_STR_LEN("*")); buffer_copy_string_len(con -> uri.path, CONST_STR_LEN("*")); http_response_insert_header(srv, con, CONST_STR_LEN("Allow") , CONST_STR_LEN("GET, POST, OPTIONS, HEAD")); con -> http_status = 200; return HANDLER_FINISHED; } else { //简化地址。 buffer_reset(con -> tmp_buf); buffer_path_simplify(con -> tmp_buf, con -> uri.path_raw); buffer_copy_string_buffer(con -> uri.path_raw, con -> tmp_buf); } //log_error_write(srv, __FILE__, __LINE__, "sb", "Simple path:", con -> uri.path_raw); /* * 得到没有解码的url地址。调用插件。 */ switch(ht = plugin_handle_url_raw(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: case HANDLER_WAIT_FOR_FD: return ht; default: log_error_write(srv, __FILE__, __LINE__, "s", "Unknown handler state."); break; } //解码url地址和query数据。 buffer_reset(con -> tmp_buf); buffer_copy_string_buffer(con -> tmp_buf, con -> uri.path_raw); buffer_urldecode_path(con -> tmp_buf); buffer_copy_string_buffer(con -> uri.path, con -> tmp_buf); //log_error_write(srv, __FILE__, __LINE__, "sb", "decode path:", con -> uri.path); buffer_urldecode_query(con -> uri.query); //log_error_write(srv, __FILE__, __LINE__, "sb", "decode query:", con -> uri.query); /* * 解码url地址。调用插件。 */ switch(ht = plugin_handle_url_clean(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: case HANDLER_WAIT_FOR_FD: return ht; default: log_error_write(srv, __FILE__, __LINE__, "s", "Unknown handler state."); break; } /* * OPTIONS方法直接返回允许。 */ if (con->request.http_method == HTTP_METHOD_OPTIONS && con->uri.path->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { /* * path 将key=val加到response的head中。 */ http_response_insert_header(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->http_status = 200; //con->file_finished = 1; return HANDLER_FINISHED; } //设置连接处理时的默认根目录。 //在调用docroot插件功能时,将根据需要重写这个根目录。 buffer_copy_string_buffer(con -> physical.doc_root, srv -> srvconf.docroot); /* * 有些插件需要设置工作根目录。 */ switch(ht = plugin_handle_docroot(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: case HANDLER_WAIT_FOR_FD: return ht; default: log_error_write(srv, __FILE__, __LINE__, "s", "Unknown handler state."); break; } //拼接物理地址。 buffer_reset(con -> tmp_buf); buffer_copy_string_buffer(con -> tmp_buf, con -> physical.doc_root); buffer_append_string_buffer(con -> tmp_buf, con -> uri.path); buffer_copy_string_buffer(con -> physical.path, con -> uri.path); buffer_path_simplify(con -> physical.real_path, con -> tmp_buf); //log_error_write(srv, __FILE__, __LINE__, "sb", "pyhsical path:", con -> physical.real_path); /* * 得到物理地址。 调用插件。 */ switch(ht = plugin_handle_physical(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: case HANDLER_WAIT_FOR_FD: return ht; default: log_error_write(srv, __FILE__, __LINE__, "s", "Unknown handler state."); break; } //检查所请求的资源是否存在。 if (-1 == response_physical_exist(srv, con, con -> physical.real_path)) { return HANDLER_FINISHED; } //如果请求的是目录,直接提示用户跳转。 if(con -> http_status != 0) { return HANDLER_FINISHED; } //资源存在,继续处理请求。 /* * 子请求开始。 * 通常,插件的主要处理工作在下面三个函数调用中。。 */ switch(ht = plugin_handle_subrequest_start(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: case HANDLER_WAIT_FOR_FD: return ht; default: log_error_write(srv, __FILE__, __LINE__, "s", "Unknown handler state."); break; } /* * 处理子请求。 * 通常是真正的对请求进行处理。 */ switch(ht = plugin_handle_handle_subrequest(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: case HANDLER_WAIT_FOR_FD: return ht; default: log_error_write(srv, __FILE__, __LINE__, "s", "Unknown handler state."); break; } /* * 子请求处理结束。 * 做一些清理标记工作。 */ switch(ht = plugin_handle_subrequest_end(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: case HANDLER_WAIT_FOR_FD: return ht; default: log_error_write(srv, __FILE__, __LINE__, "s", "Unknown handler state."); break; } //处理静态页面。 switch(ht = response_handle_static_file(srv, con)) { case HANDLER_GO_ON: break; case HANDLER_FINISHED: case HANDLER_COMEBACK: case HANDLER_WAIT_FOR_EVENT: case HANDLER_ERROR: case HANDLER_WAIT_FOR_FD: return ht; default: log_error_write(srv, __FILE__, __LINE__, "s", "Unknown handler state."); break; } /* * 到这就出错了。。。 */ return HANDLER_ERROR; }
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 http_response_finish_header(server *srv, connection *con) { if (NULL == srv || NULL == con || NULL == con -> write_queue) { return -1; } /* * 这个header有安全问题。通产默认不添加。 */ //http_response_insert_header(srv, con, CONST_STR_LEN("Server"), CONST_STR_LEN("Swiftd/0.1 written by hcy")); buffer *b = chunkqueue_get_prepend_buffer(con -> write_queue); if (NULL == b) { log_error_write(srv, __FILE__, __LINE__, "s", "chunkqueue_get_prepend_buffer failed."); return -1; } buffer_reset(b); /* * Status-Line: * HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1")); buffer_append_string_len(b, CONST_STR_LEN(" ")); //SP buffer_append_long(b, con -> http_status); buffer_append_string_len(b, CONST_STR_LEN(" ")); //SP buffer_append_string(b, get_http_status_name(con -> http_status)); buffer_append_string_len(b, CONST_STR_LEN(CRLF)); //CRLF = '\r\n' defined in base.h if(con -> keep_alive) { http_response_insert_header(srv, con, CONST_STR_LEN("Connection") , CONST_STR_LEN("keep-alive")); } else { http_response_insert_header(srv, con, CONST_STR_LEN("Connection") , CONST_STR_LEN("close")); } /* * Headers: * Key:Value CRLF */ size_t i; data_string *ds; for (i = 0; i < con -> response.headers -> used; ++i) { ds = (data_string *)con -> response.headers -> data[i]; if (NULL == ds) { continue; } buffer_append_string_buffer(b, ds -> key); buffer_append_string_len(b, CONST_STR_LEN(": ")); buffer_append_string_buffer(b, ds -> value); buffer_append_string_len(b, CONST_STR_LEN(CRLF)); } /* * header 和 message body之间的CRLF。 */ buffer_append_string_len(b, CONST_STR_LEN(CRLF)); /* * 最后一个'\0'不能发送出去!! */ -- b -> used; log_error_write(srv, __FILE__, __LINE__, "sb", "Response Headers:", b); array_reset(con -> response.headers); return 0; }
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 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; }