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; const char *auth_type = NULL; data_string *ds; mod_auth_plugin_data *p = p_d; array *req; data_string *req_method; /* 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 (buffer_is_empty(require)) continue; if (buffer_string_length(con->uri.path) < buffer_string_length(require)) 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, buffer_string_length(require))) { auth_required = 1; break; } } else { if (0 == strncmp(con->uri.path->ptr, require->ptr, buffer_string_length(require))) { 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; req_method = (data_string *)array_get_element(req, "method"); if (0 == strcmp(req_method->value->ptr, "extern")) { /* require REMOTE_USER to be already set */ if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) { con->http_status = 401; con->mode = DIRECT; return HANDLER_FINISHED; } else if (http_auth_match_rules(srv, req, ds->value->ptr, NULL, NULL)) { log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match"); con->http_status = 401; con->mode = DIRECT; return HANDLER_FINISHED; } else { return HANDLER_GO_ON; } } /* try to get Authorization-header */ if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Authorization")) && !buffer_is_empty(ds->value)) { char *auth_realm; http_authorization = ds->value->ptr; /* 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))) { auth_type = "Basic"; if (0 == strcmp(req_method->value->ptr, "basic")) { auth_satisfied = http_auth_basic_check(srv, con, p, req, auth_realm+1); } } else if ((auth_type_len == 6) && (0 == strncasecmp(http_authorization, "Digest", auth_type_len))) { auth_type = "Digest"; if (0 == strcmp(req_method->value->ptr, "digest")) { if (-1 == (auth_satisfied = http_auth_digest_check(srv, con, p, req, 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 authentication 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 */ if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) { if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string(ds->key, "REMOTE_USER"); array_insert_unique(con->environment, (data_unset *)ds); } buffer_copy_buffer(ds->value, p->auth_user); /* AUTH_TYPE environment */ if (NULL == (ds = (data_string *)array_get_element(con->environment, "AUTH_TYPE"))) { if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string(ds->key, "AUTH_TYPE"); array_insert_unique(con->environment, (data_unset *)ds); } buffer_copy_string(ds->value, auth_type); } return HANDLER_GO_ON; }
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 *auth_path = p->conf.auth_require->data[k]->key; if (auth_path->used == 0) continue; if (con->uri.path->used < auth_path->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, auth_path->ptr, auth_path->used - 1)) { auth_required = 1; break; } } else { if (0 == strncmp(con->uri.path->ptr, auth_path->ptr, auth_path->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, CONST_STR_LEN("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, CONST_STR_LEN("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 == strncmp(http_authorization, "Basic", auth_type_len))) { if (buffer_is_equal_string(method->value, CONST_STR_LEN("basic"))) { auth_satisfied = http_auth_basic_check(srv, con, p, req, con->uri.path, auth_realm+1); } } else if ((auth_type_len == 6) && (0 == strncmp(http_authorization, "Digest", auth_type_len))) { if (buffer_is_equal_string(method->value, CONST_STR_LEN("digest"))) { if (-1 == (auth_satisfied = http_auth_digest_check(srv, con, p, req, con->uri.path, auth_realm+1))) { con->http_status = 400; /* 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, CONST_STR_LEN("method")); realm = (data_string *)array_get_element(req, CONST_STR_LEN("realm")); con->http_status = 401; if (buffer_is_equal_string(method->value, CONST_STR_LEN("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 (buffer_is_equal_string(method->value, CONST_STR_LEN("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; }
int http_request_parse(server *srv, connection *con) { char *uri = NULL, *proto = NULL, *method = NULL, con_length_set; int is_key = 1, key_len = 0, is_ws_after_key = 0, in_folding; char *value = NULL, *key = NULL; char *reqline_host = NULL; int reqline_hostlen = 0; enum { HTTP_CONNECTION_UNSET, HTTP_CONNECTION_KEEPALIVE, HTTP_CONNECTION_CLOSE } keep_alive_set = HTTP_CONNECTION_UNSET; int line = 0; int request_line_stage = 0; size_t i, first, ilen; int done = 0; const unsigned int http_header_strict = (con->conf.http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); /* * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$" * Option : "^([-a-zA-Z]+): (.+)$" * End : "^$" */ if (con->conf.log_request_header) { log_error_write(srv, __FILE__, __LINE__, "sdsdSb", "fd:", con->fd, "request-len:", buffer_string_length(con->request.request), "\n", con->request.request); } if (con->request_count > 1 && con->request.request->ptr[0] == '\r' && con->request.request->ptr[1] == '\n') { /* we are in keep-alive and might get \r\n after a previous POST request.*/ buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, buffer_string_length(con->request.request) - 2); } else { /* fill the local request buffer */ buffer_copy_buffer(con->parse_request, con->request.request); } keep_alive_set = 0; con_length_set = 0; /* parse the first line of the request * * should be: * * <method> <uri> <protocol>\r\n * */ ilen = buffer_string_length(con->parse_request); for (i = 0, first = 0; i < ilen && line == 0; i++) { switch(con->parse_request->ptr[i]) { case '\r': if (con->parse_request->ptr[i+1] == '\n') { http_method_t r; char *nuri = NULL; size_t j, jlen; /* \r\n -> \0\0 */ con->parse_request->ptr[i] = '\0'; con->parse_request->ptr[i+1] = '\0'; buffer_copy_string_len(con->request.request_line, con->parse_request->ptr, i); if (request_line_stage != 2) { con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } proto = con->parse_request->ptr + first; *(uri - 1) = '\0'; *(proto - 1) = '\0'; /* we got the first one :) */ if (HTTP_METHOD_UNSET == (r = get_http_method_key(method))) { con->http_status = 501; con->response.keep_alive = 0; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } con->request.http_method = r; /* * RFC2616 says: * * HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT * * */ if (0 == strncmp(proto, "HTTP/", sizeof("HTTP/") - 1)) { char * major = proto + sizeof("HTTP/") - 1; char * minor = strchr(major, '.'); char *err = NULL; int major_num = 0, minor_num = 0; int invalid_version = 0; if (NULL == minor || /* no dot */ minor == major || /* no major */ *(minor + 1) == '\0' /* no minor */) { invalid_version = 1; } else { *minor = '\0'; major_num = strtol(major, &err, 10); if (*err != '\0') invalid_version = 1; *minor++ = '.'; minor_num = strtol(minor, &err, 10); if (*err != '\0') invalid_version = 1; } if (invalid_version) { con->http_status = 400; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } if (major_num == 1 && minor_num == 1) { con->request.http_version = con->conf.allow_http11 ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0; } else if (major_num == 1 && minor_num == 0) { con->request.http_version = HTTP_VERSION_1_0; } else { con->http_status = 505; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "unknown HTTP version -> 505"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } } else { con->http_status = 400; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } if (0 == strncmp(uri, "http://", 7) && NULL != (nuri = strchr(uri + 7, '/'))) { reqline_host = uri + 7; reqline_hostlen = nuri - reqline_host; buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1); } else if (0 == strncmp(uri, "https://", 8) && NULL != (nuri = strchr(uri + 8, '/'))) { reqline_host = uri + 8; reqline_hostlen = nuri - reqline_host; buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1); } else { /* everything looks good so far */ buffer_copy_string_len(con->request.uri, uri, proto - uri - 1); } /* check uri for invalid characters */ jlen = buffer_string_length(con->request.uri); if (http_header_strict) { for (j = 0; j < jlen && request_uri_is_valid_char(con->request.uri->ptr[j]); j++) ; } else { char *z = memchr(con->request.uri->ptr, '\0', jlen); j = (NULL == z) ? jlen : (size_t)(z - con->request.uri->ptr); } if (j < jlen) { con->http_status = 400; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { unsigned char buf[2]; buf[0] = con->request.uri->ptr[j]; buf[1] = '\0'; if (con->request.uri->ptr[j] > 32 && con->request.uri->ptr[j] != 127) { /* the character is printable -> print it */ log_error_write(srv, __FILE__, __LINE__, "ss", "invalid character in URI -> 400", buf); } else { /* a control-character, print ascii-code */ log_error_write(srv, __FILE__, __LINE__, "sd", "invalid character in URI -> 400", con->request.uri->ptr[j]); } log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } buffer_copy_buffer(con->request.orig_uri, con->request.uri); con->http_status = 0; i++; line++; first = i+1; } break; case ' ': switch(request_line_stage) { case 0: /* GET|POST|... */ method = con->parse_request->ptr + first; first = i + 1; break; case 1: /* /foobar/... */ uri = con->parse_request->ptr + first; first = i + 1; break; default: /* ERROR, one space to much */ con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "overlong request line -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } request_line_stage++; break; } } in_folding = 0; if (buffer_string_is_empty(con->request.uri)) { con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "no uri specified -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } if (reqline_host) { /* Insert as host header */ data_string *ds; if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string_len(ds->key, CONST_STR_LEN("Host")); buffer_copy_string_len(ds->value, reqline_host, reqline_hostlen); array_insert_unique(con->request.headers, (data_unset *)ds); con->request.http_host = ds->value; } for (; i <= ilen && !done; i++) { char *cur = con->parse_request->ptr + i; if (is_key) { size_t j; int got_colon = 0; /** * 1*<any CHAR except CTLs or separators> * CTLs == 0-31 + 127, CHAR = 7-bit ascii (0..127) * */ switch(*cur) { case ':': is_key = 0; value = cur + 1; if (is_ws_after_key == 0) { key_len = i - first; } is_ws_after_key = 0; break; case '(': case ')': case '<': case '>': case '@': case ',': case ';': case '\\': case '\"': case '/': case '[': case ']': case '?': case '=': case '{': case '}': con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "sbsds", "invalid character in key", con->request.request, cur, *cur, "-> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; case ' ': case '\t': if (i == first) { is_key = 0; in_folding = 1; value = cur; break; } key_len = i - first; /* skip every thing up to the : */ for (j = 1; !got_colon; j++) { switch(con->parse_request->ptr[j + i]) { case ' ': case '\t': /* skip WS */ continue; case ':': /* ok, done; handle the colon the usual way */ i += j - 1; got_colon = 1; is_ws_after_key = 1; /* we already know the key length */ break; default: /* error */ if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "WS character in key -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; return 0; } } break; case '\r': if (con->parse_request->ptr[i+1] == '\n' && i == first) { /* End of Header */ con->parse_request->ptr[i] = '\0'; con->parse_request->ptr[i+1] = '\0'; i++; done = 1; } else { if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "CR without LF -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; return 0; } break; default: if (http_header_strict ? (*cur < 32 || ((unsigned char)*cur) >= 127) : *cur == '\0') { con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "sbsds", "invalid character in key", con->request.request, cur, *cur, "-> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } /* ok */ break; } } else { switch(*cur) { case '\r': if (con->parse_request->ptr[i+1] == '\n') { data_string *ds = NULL; /* End of Headerline */ con->parse_request->ptr[i] = '\0'; con->parse_request->ptr[i+1] = '\0'; if (in_folding) { buffer *key_b; /** * we use a evil hack to handle the line-folding * * As array_insert_unique() deletes 'ds' in the case of a duplicate * ds points somewhere and we get a evil crash. As a solution we keep the old * "key" and get the current value from the hash and append us * * */ if (!key || !key_len) { /* 400 */ if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "WS at the start of first line -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; return 0; } key_b = buffer_init(); buffer_copy_string_len(key_b, key, key_len); if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key_b->ptr))) { buffer_append_string(ds->value, value); } buffer_free(key_b); } else { int s_len; key = con->parse_request->ptr + first; s_len = cur - value; /* strip trailing white-spaces */ for (; s_len > 0 && (value[s_len - 1] == ' ' || value[s_len - 1] == '\t'); s_len--); value[s_len] = '\0'; if (s_len > 0) { int cmp = 0; if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string_len(ds->key, key, key_len); buffer_copy_string_len(ds->value, value, s_len); /* retreive values * * * the list of options is sorted to simplify the search */ if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) { array *vals; size_t vi; /* split on , */ vals = srv->split_vals; array_reset(vals); http_request_split_value(vals, ds->value); for (vi = 0; vi < vals->used; vi++) { data_string *dsv = (data_string *)vals->data[vi]; if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("keep-alive"))) { keep_alive_set = HTTP_CONNECTION_KEEPALIVE; break; } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) { keep_alive_set = HTTP_CONNECTION_CLOSE; break; } } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) { char *err; unsigned long int r; size_t j, jlen; if (con_length_set) { con->http_status = 400; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "duplicate Content-Length-header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } jlen = buffer_string_length(ds->value); for (j = 0; j < jlen; j++) { char c = ds->value->ptr[j]; if (!isdigit((unsigned char)c)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "content-length broken:", ds->value, "-> 400"); con->http_status = 400; con->keep_alive = 0; array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } r = strtoul(ds->value->ptr, &err, 10); if (*err == '\0') { con_length_set = 1; con->request.content_length = r; } else { log_error_write(srv, __FILE__, __LINE__, "sbs", "content-length broken:", ds->value, "-> 400"); con->http_status = 400; con->keep_alive = 0; array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Type")))) { /* if dup, only the first one will survive */ if (!con->request.http_content_type) { con->request.http_content_type = ds->value->ptr; } else { con->http_status = 400; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "duplicate Content-Type-header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Expect")))) { /* HTTP 2616 8.2.3 * Expect: 100-continue * * -> (10.1.1) 100 (read content, process request, send final status-code) * -> (10.4.18) 417 (close) * * (not handled at all yet, we always send 417 here) * * What has to be added ? * 1. handling of chunked request body * 2. out-of-order sending from the HTTP/1.1 100 Continue * header * */ if (srv->srvconf.reject_expect_100_with_417 && 0 == buffer_caseless_compare(CONST_BUF_LEN(ds->value), CONST_STR_LEN("100-continue"))) { con->http_status = 417; con->keep_alive = 0; array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) { if (reqline_host) { /* ignore all host: headers as we got the host in the request line */ ds->free((data_unset*) ds); ds = NULL; } else if (!con->request.http_host) { con->request.http_host = ds->value; } else { con->http_status = 400; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "duplicate Host-header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) { /* Proxies sometimes send dup headers * if they are the same we ignore the second * if not, we raise an error */ if (!con->request.http_if_modified_since) { con->request.http_if_modified_since = ds->value->ptr; } else if (0 == strcasecmp(con->request.http_if_modified_since, ds->value->ptr)) { /* ignore it if they are the same */ ds->free((data_unset *)ds); ds = NULL; } else { con->http_status = 400; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "duplicate If-Modified-Since header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) { /* if dup, only the first one will survive */ if (!con->request.http_if_none_match) { con->request.http_if_none_match = ds->value->ptr; } else { ds->free((data_unset*) ds); ds = NULL; } } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) { if (!con->request.http_range) { /* bytes=.*-.* */ if (0 == strncasecmp(ds->value->ptr, "bytes=", 6) && NULL != strchr(ds->value->ptr+6, '-')) { /* if dup, only the first one will survive */ con->request.http_range = ds->value->ptr + 6; } } else { con->http_status = 400; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "duplicate Range-header -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } array_insert_unique(con->request.headers, (data_unset *)ds); return 0; } } if (ds) array_insert_unique(con->request.headers, (data_unset *)ds); } else { /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */ } } i++; first = i+1; is_key = 1; value = NULL; #if 0 /** * for Bug 1230 keep the key_len a live */ key_len = 0; #endif in_folding = 0; } else { if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "sbs", "CR without LF", con->request.request, "-> 400"); } con->http_status = 400; con->keep_alive = 0; con->response.keep_alive = 0; return 0; } break; case ' ': case '\t': /* strip leading WS */ if (value == cur) value = cur+1; break; default: if (http_header_strict ? (*cur >= 0 && *cur < 32) : *cur == '\0') { if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "sds", "invalid char in header", (int)*cur, "-> 400"); } con->http_status = 400; con->keep_alive = 0; return 0; } break; } } } con->header_len = i; /* do some post-processing */ if (con->request.http_version == HTTP_VERSION_1_1) { if (keep_alive_set != HTTP_CONNECTION_CLOSE) { /* no Connection-Header sent */ /* HTTP/1.1 -> keep-alive default TRUE */ con->keep_alive = 1; } else { con->keep_alive = 0; } /* RFC 2616, 14.23 */ if (con->request.http_host == NULL || buffer_string_is_empty(con->request.http_host)) { con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } return 0; } } else { if (keep_alive_set == HTTP_CONNECTION_KEEPALIVE) { /* no Connection-Header sent */ /* HTTP/1.0 -> keep-alive default FALSE */ con->keep_alive = 1; } else { con->keep_alive = 0; } } /* check hostname field if it is set */ if (!buffer_is_empty(con->request.http_host) && (((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_STRICT) && 0 != request_check_hostname(con->request.http_host)) || ((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_NORMALIZE) && 0 != http_request_host_normalize(con->request.http_host)))) { if (srv->srvconf.log_request_header_on_error) { log_error_write(srv, __FILE__, __LINE__, "s", "Invalid Hostname -> 400"); log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); } con->http_status = 400; con->response.keep_alive = 0; con->keep_alive = 0; return 0; } switch(con->request.http_method) { case HTTP_METHOD_GET: case HTTP_METHOD_HEAD: /* content-length is forbidden for those */ if (con_length_set && con->request.content_length != 0) { /* content-length is missing */ log_error_write(srv, __FILE__, __LINE__, "s", "GET/HEAD with content-length -> 400"); con->keep_alive = 0; con->http_status = 400; return 0; } break; case HTTP_METHOD_POST: /* content-length is required for them */ if (!con_length_set) { /* content-length is missing */ log_error_write(srv, __FILE__, __LINE__, "s", "POST-request, but content-length missing -> 411"); con->keep_alive = 0; con->http_status = 411; return 0; } break; default: /* require Content-Length if request contains request body */ if (array_get_element(con->request.headers, "Transfer-Encoding")) { /* presence of Transfer-Encoding in request headers requires "chunked" * be final encoding in HTTP/1.1. Return 411 Length Required as * lighttpd does not support request input transfer-encodings */ con->keep_alive = 0; con->http_status = 411; /* 411 Length Required */ return 0; } break; } /* check if we have read post data */ if (con_length_set) { /* don't handle more the SSIZE_MAX bytes in content-length */ if (con->request.content_length > SSIZE_MAX) { con->http_status = 413; con->keep_alive = 0; log_error_write(srv, __FILE__, __LINE__, "sos", "request-size too long:", (off_t) con->request.content_length, "-> 413"); return 0; } /* we have content */ if (con->request.content_length != 0) { return 1; } } return 0; }
static int connection_handle_write_prepare(server *srv, connection *con) { if (con->mode == DIRECT) { /* static files */ switch(con->request.http_method) { case HTTP_METHOD_GET: case HTTP_METHOD_POST: case HTTP_METHOD_HEAD: break; case HTTP_METHOD_OPTIONS: /* * 400 is coming from the request-parser BEFORE uri.path is set * 403 is from the response handler when noone else catched it * * */ if ((!con->http_status || con->http_status == 200) && !buffer_string_is_empty(con->uri.path) && con->uri.path->ptr[0] != '*') { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; con->http_status = 200; con->file_finished = 1; chunkqueue_reset(con->write_queue); } break; default: if (0 == con->http_status) { con->http_status = 501; } break; } } if (con->http_status == 0) { con->http_status = 403; } switch(con->http_status) { case 204: /* class: header only */ case 205: case 304: /* disable chunked encoding again as we have no body */ con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; chunkqueue_reset(con->write_queue); con->file_finished = 1; break; default: /* class: header + body */ if (con->mode != DIRECT) break; /* only custom body for 4xx and 5xx */ if (con->http_status < 400 || con->http_status >= 600) break; con->file_finished = 0; connection_handle_errdoc_init(srv, con); /* try to send static errorfile */ if (!buffer_string_is_empty(con->conf.errorfile_prefix)) { stat_cache_entry *sce = NULL; buffer_copy_buffer(con->physical.path, con->conf.errorfile_prefix); buffer_append_int(con->physical.path, con->http_status); buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); if (0 == http_chunk_append_file(srv, con, con->physical.path)) { con->file_finished = 1; if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); } } } if (!con->file_finished) { buffer *b; buffer_reset(con->physical.path); con->file_finished = 1; b = buffer_init(); /* build default error-page */ buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" " <title>")); buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN( "</title>\n" " </head>\n" " <body>\n" " <h1>")); buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN("</h1>\n" " </body>\n" "</html>\n" )); (void)http_chunk_append_buffer(srv, con, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } break; } /* Allow filter plugins to change response headers before they are written. */ switch(plugins_call_handle_response_start(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "s", "response_start plugin failed"); return -1; } if (con->file_finished) { /* we have all the content and chunked encoding is not used, set a content-length */ if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) { off_t qlen = chunkqueue_length(con->write_queue); /** * The Content-Length header only can be sent if we have content: * - HEAD doesn't have a content-body (but have a content-length) * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3) * * Otherwise generate a Content-Length header as chunked encoding is not * available */ if ((con->http_status >= 100 && con->http_status < 200) || con->http_status == 204 || con->http_status == 304) { data_string *ds; /* no Content-Body, no Content-Length */ if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) { buffer_reset(ds->value); /* Headers with empty values are ignored for output */ } } else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) { /* qlen = 0 is important for Redirects (301, ...) as they MAY have * a content. Browsers are waiting for a Content otherwise */ buffer_copy_int(srv->tmp_buf, qlen); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); } } } else { /** * the file isn't finished yet, but we have all headers * * to get keep-alive we either need: * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or * - Transfer-Encoding: chunked (HTTP/1.1) */ if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) && ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) { if (con->request.http_version == HTTP_VERSION_1_1) { off_t qlen = chunkqueue_length(con->write_queue); con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; if (qlen) { /* create initial Transfer-Encoding: chunked segment */ buffer *b = srv->tmp_chunk_len; buffer_string_set_length(b, 0); buffer_append_uint_hex(b, (uintmax_t)qlen); buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_prepend_buffer(con->write_queue, b); chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("\r\n")); } } else { con->keep_alive = 0; } } /** * if the backend sent a Connection: close, follow the wish * * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we * will close the connection. That's fine. We can always decide the close * the connection * * FIXME: to be nice we should remove the Connection: ... */ if (con->parsed_response & HTTP_CONNECTION) { /* a subrequest disable keep-alive although the client wanted it */ if (con->keep_alive && !con->response.keep_alive) { con->keep_alive = 0; } } } if (con->request.http_method == HTTP_METHOD_HEAD) { /** * a HEAD request has the same as a GET * without the content */ con->file_finished = 1; chunkqueue_reset(con->write_queue); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; } http_response_write_header(srv, con); return 0; }
static 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_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 (ck_colon == val_colon) { /* nothing to do with it */ break; } if (ck_colon) { /* condition "host:port" but client send "host" */ buffer_copy_string_buffer(srv->cond_check_buf, l); buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":")); buffer_append_long(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr))); l = srv->cond_check_buf; } else if (!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_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; } /* 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 = htonl(~((1 << (32 - nm_bits)) - 1)); 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, l->ptr, l->used - 1, 0, 0, cache->matches, elementsof(cache->matches)); cache->patterncount = n; if (n > 0) { cache->comp_value = l; cache->comp_type = dc->comp; return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { /* cache is already cleared */ return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } break; } #endif default: /* no way */ break; } return COND_RESULT_FALSE; }
static smb_info_t *smbdav_get_smb_info_from_pool(server *srv, connection *con, plugin_data *p) { smb_info_t *c; if(p->smb_info_list==NULL||con->mode==DIRECT) return NULL; //- Get user-Agent data_string *ds = (data_string *)array_get_element(con->request.headers, "user-Agent"); if(ds==NULL){ return NULL; } char pWorkgroup[30]={0}; char pServer[64]={0}; char pShare[1280]={0}; char pPath[1280]={0}; smbc_wrapper_parse_path2(con, pWorkgroup, pServer, pShare, pPath); buffer* buffer_server = buffer_init(); if(pServer[0] != '\0') buffer_append_string(buffer_server,pServer); buffer* buffer_share = buffer_init(); if(pShare[0] != '\0') buffer_append_string(buffer_share,pShare); int count = 0; for (c = p->smb_info_list; c; c = c->next) { count++; if(!buffer_is_equal(c->server, buffer_server)) continue; //Cdbg(DBE, "c->share=[%s], buffer_share=[%s]", c->share->ptr, buffer_share->ptr); //if(con->mode==SMB_BASIC && !buffer_is_equal(c->share, buffer_share)) // continue; //Cdbg(DBE, "%d, c->src_ip=[%s], dst_addr_buf=[%s]", count, c->src_ip->ptr, con->dst_addr_buf->ptr); if(!buffer_is_equal(c->src_ip, con->dst_addr_buf)) continue; Cdbg(DBE, "%d, c->user_agent=[%s], user_agent=[%s]", count, c->user_agent->ptr, ds->value->ptr); if(!buffer_is_equal(c->user_agent, ds->value)){ continue; } //Cdbg(DBE, "return %d, c->server=[%s]", count, c->server->ptr); buffer_free(buffer_server); buffer_free(buffer_share); return c; } buffer_free(buffer_server); buffer_free(buffer_share); return NULL; }
static int http_auth_match_rules(server *srv, mod_auth_plugin_data *p, const char *url, const char *username, const char *group, const char *host) { const char *r = NULL, *rules = NULL; size_t i; int username_len; data_string *require; array *req; UNUSED(group); UNUSED(host); /* check what has to be match to fullfil the request */ /* search auth-directives for path */ for (i = 0; i < p->conf.auth_require->used; i++) { if (p->conf.auth_require->data[i]->key->used == 0) continue; if (0 == strncmp(url, p->conf.auth_require->data[i]->key->ptr, p->conf.auth_require->data[i]->key->used - 1)) { break; } } if (i == p->conf.auth_require->used) { return -1; } req = ((data_array *)(p->conf.auth_require->data[i]))->value; require = (data_string *)array_get_element(req, "require"); /* if we get here, the user we got a authed user */ if (0 == strcmp(require->value->ptr, "valid-user")) { return 0; } /* user=name1|group=name3|host=name4 */ /* separate the string by | */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value); #endif username_len = username ? strlen(username) : 0; r = rules = require->value->ptr; while (1) { const char *eq; const char *k, *v, *e; int k_len, v_len, r_len; e = strchr(r, '|'); if (e) { r_len = e - r; } else { r_len = strlen(rules) - (r - rules); } /* from r to r + r_len is a rule */ if (0 == strncmp(r, "valid-user", r_len)) { log_error_write(srv, __FILE__, __LINE__, "sb", "parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules", require->value); return -1; } /* search for = in the rules */ if (NULL == (eq = strchr(r, '='))) { log_error_write(srv, __FILE__, __LINE__, "sb", "parsing the 'require' section in 'auth.require' failed: a = is missing", require->value); return -1; } /* = out of range */ if (eq > r + r_len) { log_error_write(srv, __FILE__, __LINE__, "sb", "parsing the 'require' section in 'auth.require' failed: = out of range", require->value); return -1; } /* the part before the = is user|group|host */ k = r; k_len = eq - r; v = eq + 1; v_len = r_len - k_len - 1; if (k_len == 4) { if (0 == strncmp(k, "user", k_len)) { if (username && username_len == v_len && 0 == strncmp(username, v, v_len)) { return 0; } } else if (0 == strncmp(k, "host", k_len)) { log_error_write(srv, __FILE__, __LINE__, "s", "host ... (not implemented)"); } else { log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); return -1; } } else if (k_len == 5) { if (0 == strncmp(k, "group", k_len)) { log_error_write(srv, __FILE__, __LINE__, "s", "group ... (not implemented)"); } else { log_error_write(srv, __FILE__, __LINE__, "ss", "unknown key", k); return -1; } } else { log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); return -1; } if (!e) break; r = e + 1; } log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched"); return -1; }
/* ****************** * 程序的入口点 ***************** */ int main(int argc, char **argv) { server *srv = NULL; int print_config = 0; int test_config = 0; int i_am_root; int o; int num_childs = 0; int pid_fd = -1, fd; size_t i; #ifdef HAVE_SIGACTION struct sigaction act; #endif #ifdef HAVE_GETRLIMIT struct rlimit rlim; #endif #ifdef USE_ALARM struct itimerval interval; interval.it_interval.tv_sec = 1; interval.it_interval.tv_usec = 0; interval.it_value.tv_sec = 1; interval.it_value.tv_usec = 0; #endif /* * for nice %b handling in strfime() */ setlocale(LC_TIME, "C"); if (NULL == (srv = server_init())) { fprintf(stderr, "did this really happen?\n"); return -1; } /* * init structs done */ srv->srvconf.port = 0; // #ifdef HAVE_GETUID i_am_root = (getuid() == 0); #else i_am_root = 0; #endif //程序将被设置为守护进程。 srv->srvconf.dont_daemonize = 0; //处理参数。 while (-1 != (o = getopt(argc, argv, "f:m:hvVDpt"))) { switch (o) { case 'f': if (config_read(srv, optarg)) { server_free(srv); return -1; } break; case 'm': buffer_copy_string(srv->srvconf.modules_dir, optarg); break; case 'p': print_config = 1; break; case 't': test_config = 1; break; case 'D': srv->srvconf.dont_daemonize = 1; break; case 'v': show_version(); return 0; case 'V': show_features(); return 0; case 'h': show_help(); return 0; default: show_help(); server_free(srv); return -1; } } if (!srv->config_storage) { log_error_write(srv, __FILE__, __LINE__, "s", "No configuration available. Try using -f option."); server_free(srv); return -1; } if (print_config) { data_unset *dc = srv->config_context->data[0]; if (dc) { dc->print(dc, 0); fprintf(stdout, "\n"); } else { /* * shouldn't happend */ fprintf(stderr, "global config not found\n"); } } if (test_config) //没有进行任何测试。。。 { printf("Syntax OK\n"); } if (test_config || print_config) { server_free(srv); return 0; } /* * close stdin and stdout, as they are not needed * 关闭标准输入和标准输出。 */ openDevNull(STDIN_FILENO); openDevNull(STDOUT_FILENO); //设置为默认的配置。 if (0 != config_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting default values failed"); server_free(srv); return -1; } /* * UID handling */ #ifdef HAVE_GETUID //检查有效用户ID和有效组ID是否是0(root)。 if (!i_am_root && (geteuid() == 0 || getegid() == 0)) { /* * we are setuid-root * 程序的实际用户ID不是0,也就是程序不是由超级用户运行的,但是程序的有效用户ID * 或者有效组ID是超级用户(组),因此,程序可以访问任何文件而不受限制!这样很 * 不安全。因此程序退出并提示用户。 */ log_error_write(srv, __FILE__, __LINE__, "s", "Are you nuts ? Don't apply a SUID bit to this binary"); server_free(srv); return -1; } #endif /* * check document-root */ if (srv->config_storage[0]->document_root->used <= 1) { log_error_write(srv, __FILE__, __LINE__, "s", "document-root is not set\n"); server_free(srv); return -1; } /* * 加载插件 * * 插件是以动态链接库的形式存在的。在配置文件中,要配置好插件的链接库的 * 路径位置和库中函数的名称。在这个函数中,通过这些配置,获得函数的入口 * 地址。 * 可以看到,这个函数的调用是在整个程序的初始化阶段,而且只调用了这一次, * 因此,在服务器运行之前,要配置好所有的插件。 * 如果想增加插件,只能重启服务器。 * * 在实现plugins_load函数的时候,同时也有一个用于加载静态链接库的版本, * 但函数并没有什么实质性的实现。 * */ if (plugins_load(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "loading plugins finally failed"); plugins_free(srv); server_free(srv); return -1; } /* * open pid file BEFORE chroot * 打开pid文件,并将进程号写入pid文件。 */ if (srv->srvconf.pid_file->used) { if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) /** * O_EXCL和O_CREAT同时使用,测试文件是否存在,如果存在 * 则报错。 */ { //pid文件打开失败。。。 struct stat st; if (errno != EEXIST) //不是报文件已经存在的错误。 { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } //pid文件已经存在,测试文件的状态。 if (0 != stat(srv->srvconf.pid_file->ptr, &st)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "stating existing pid-file failed:", srv->srvconf.pid_file, strerror(errno)); } if (!S_ISREG(st.st_mode)) //pid文件是普通文件。 { log_error_write(srv, __FILE__, __LINE__, "sb", "pid-file exists and isn't regular file:", srv->srvconf.pid_file); return -1; } //重新打开pid文件。 //这里不在使用O_EXCL参数,由于pid文件已经存在且是普通文件,则覆盖原先的文件。 if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* * select limits itself as it is a hard limit and will lead to a segfault * we add some safety * select的硬限制。减去200是为了增加安全性,防止出现段错误。 */ srv->max_fds = FD_SETSIZE - 200; } else { srv->max_fds = 4096; } //程序是在超级用户模式下运行的。 if (i_am_root) { struct group *grp = NULL; struct passwd *pwd = NULL; int use_rlimit = 1; #ifdef HAVE_VALGRIND_VALGRIND_H if (RUNNING_ON_VALGRIND) use_rlimit = 0; #endif #ifdef HAVE_GETRLIMIT /** * getrlimit和setrlimit函数用于查询和修改进程的资源限制。 * * include <sys/resource.h> * int getrlimit(int resource, struct rlimit *rlim); * int setrlimit(int resource, const struct rlimit *rlim); * 返回:若成功为0,出错为非0 * * 对这两个函数的每一次调用都指定一个资源以及一个指向下列结构的指针。 * * struct rlimit * { * rlim_t rlim_cur; //软限制:当前限制 * rlim_t rlim_max; //硬限制:rlimcur的最大值 * }; * * 这两个函数不属于POSIX.1,但SVR4和4.3+BSD提供它们。SVR4在上面的结构中使用基本系统数据类型rlim_t。 * 其它系统则将这两个成员定义为整型或长整型。 * * 程序中使用的参数RLIMIT_NOFILE:Specifies a value one greater than the maximum file descriptor * number that can be opened by this process. 设置最大的文件打开数,且实际打开的文件数要比这个数 * 小一。 * * 详细使用:man getrlimit */ if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) //获得当前的文件打开数限制。 { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } if (use_rlimit && srv->srvconf.max_fds) { /* * set rlimits. 设置限制。 */ rlim.rlim_cur = srv->srvconf.max_fds; //软限制。 rlim.rlim_max = srv->srvconf.max_fds; //硬限制。 if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } //根据实际设置情况,重新设置max_fds。 if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* * set core file rlimit, if enable_cores is set * 设置core文件的限制。如果设置了enable_cores。 */ if (use_rlimit && srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* * don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > FD_SETSIZE - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } #ifdef HAVE_PWD_H /* * set user and group 设置用户和组。 */ if (srv->srvconf.username->used) { //根据配置中的用户名获取用户信息。 if (NULL == (pwd = getpwnam(srv->srvconf.username->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find username", srv->srvconf.username); return -1; } if (pwd->pw_uid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set uid to 0\n"); return -1; } } if (srv->srvconf.groupname->used) { //根据上面得到的用户所在的组的组名,获得组的信息。 if (NULL == (grp = getgrnam(srv->srvconf.groupname->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find groupname", srv->srvconf.groupname); return -1; } if (grp->gr_gid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set gid to 0\n"); return -1; } } #endif /* * we need root-perms for port < 1024 * 使用超级用户模式获得小于1024的端口。初始化网络。 * 创建监听socket,绑定地址并开始监听。 */ if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } #ifdef HAVE_PWD_H /* * Change group before chroot, when we have access * to /etc/group * */ if (srv->srvconf.groupname->used) { setgid(grp->gr_gid); setgroups(0, NULL); //返回用户组的数目。 if (srv->srvconf.username->used) { //Initialize the group access list by reading the group database /etc/group and using all groups of which //user is a member. The additional group group is also added to the list. initgroups(srv->srvconf.username->ptr, grp->gr_gid); } } #endif #ifdef HAVE_CHROOT if (srv->srvconf.changeroot->used) { //The tzset() function initializes the tzname variable from the TZ environment variable. //This function is automatically called by the other time conversion functions that depend //on the time zone. //In a SysV-like environment it will also set the variables time-zone (seconds West of GMT) //and daylight //(0 if this time zone does not have any daylight saving time rules, nonzero if there is a //time during the year when daylight saving time applies). tzset(); //设置程序所参考的根目录,将被所有的子进程继承。 //也就是对于本程序而言,"/"并不是系统的根目录,而是这设置的目录。 if (-1 == chroot(srv->srvconf.changeroot->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ss", "chroot failed: ", strerror(errno)); return -1; } //修改工作目录. /* * 注意: * 由于前面已经设置了根目录。因此这里将工作目录切换到"/"并不是系统的根目录,而是 * 上面通过函数chroot设置的根目录。 */ if (-1 == chdir("/")) { log_error_write(srv, __FILE__, __LINE__, "ss", "chdir failed: ", strerror(errno)); return -1; } } #endif #ifdef HAVE_PWD_H /* * drop root privs 放弃超级管理员权限。 */ if (srv->srvconf.username->used) { setuid(pwd->pw_uid); } #endif #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) /** * on IRIX 6.5.30 they have prctl() but no DUMPABLE */ if (srv->srvconf.enable_cores) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif } /* * 下面的是程序在非root用户下执行的设置。 */ else { #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } /** * we are not root can can't increase the fd-limit, but we can reduce it * 我们不是root,不能增加fd-limit,但我们可以减少。 */ if (srv->srvconf.max_fds && srv->srvconf.max_fds < rlim.rlim_cur) { /* * set rlimits */ rlim.rlim_cur = srv->srvconf.max_fds; //只能设置软限制。 if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* * set core file rlimit, if enable_cores is set */ if (srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* * don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > FD_SETSIZE - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } } /* * set max-conns 设置最大连接数。 */ if (srv->srvconf.max_conns > srv->max_fds) { /* * we can't have more connections than max-fds * 最大连接数要小于最大文件打开数(max-fds) */ srv->max_conns = srv->max_fds; } else if (srv->srvconf.max_conns) { /* * otherwise respect the wishes of the user * 根据用户设置。 */ srv->max_conns = srv->srvconf.max_conns; } else { /* * or use the default 默认。 */ srv->max_conns = srv->max_fds; } /* * 在前面的plugins_load函数中,已经所有的插件读入到系统中,并对插件中含有 * 的各种函数,确定入口地址。 * 在这里,程序对所有插件进行登记造册,确定插件中含有的功能,并计入表中(srv->plugins_slot) * */ if (HANDLER_GO_ON != plugins_call_init(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Initialization of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_FORK /* * network is up, let's deamonize ourself * 设置为守护进程。 */ if (srv->srvconf.dont_daemonize == 0) daemonize(); #endif srv->gid = getgid(); srv->uid = getuid(); /* * write pid file 写pid文件。 */ if (pid_fd != -1) { buffer_copy_long(srv->tmp_buf, getpid()); buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1); close(pid_fd); pid_fd = -1; } /* * Close stderr ASAP in the child process to make sure that nothing is * being written to that fd which may not be valid anymore. * 关闭向标准输出的输出,打开日志文件。 */ if (-1 == log_error_open(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } //将插件设置为默认配置 if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* * dump unused config-keys */ for (i = 0; i < srv->config_context->used; i++) { array *config = ((data_config *) srv->config_context->data[i])->value; size_t j; for (j = 0; config && j < config->used; j++) { data_unset *du = config->data[j]; /* * all var.* is known as user defined variable */ if (strncmp(du->key->ptr, "var.", sizeof("var.") - 1) == 0) { continue; } if (NULL == array_get_element(srv->config_touched, du->key->ptr)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "WARNING: unknown config-key:", du->key, "(ignored)"); } } } if (srv->config_unsupported) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains unsupported keys. Going down."); } if (srv->config_deprecated) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains deprecated keys. Going down."); } if (srv->config_unsupported || srv->config_deprecated) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } //设置一些信号的处理方法。 //SIGPIPE:在写管道时,读管道的进程终止,产生此信号。 #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); sigaction(SIGUSR1, &act, NULL); # if defined(SA_SIGINFO) act.sa_sigaction = sigaction_handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; # else act.sa_handler = signal_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; # endif sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); #elif defined(HAVE_SIGNAL) /* * ignore the SIGPIPE from sendfile() */ signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGALRM, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); signal(SIGCHLD, signal_handler); signal(SIGINT, signal_handler); #endif #ifdef USE_ALARM signal(SIGALRM, signal_handler); /* * setup periodic timer (1 second) * The system provides each process with three interval timers, each decrementing in a distinct time domain. * When any timer expires a signal is sent to the process, and the timer (potentially) restarts. * * ITIMER_REAL decrements in real time, and delivers SIGALRM upon expiration. * ITIMER_VIRTUAL decrements only when the process is executing, and delivers SIGVTALRM upon expiration. * ITIMER_PROF decrements both when the process executes and when the system is executing on * behalf of the process. * Coupled with ITIMER_VIRTUAL, this timer is usually used to profile the time spent * by the application in user and kernel space. * SIGPROF is delivered upon expiration. * Timer values are defined by the following structures: * struct itimerval * { * struct timeval it_interval; //next value * struct timeval it_value; //current value * }; * struct timeval * { * long tv_sec; // seconds * long tv_usec; //microseconds * }; * The function getitimer() fills the structure indicated by value with the current setting for the timer * indicated by which (one of ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF). The element it_value is * set to the amount of time remaining on the timer, or zero ifthe timer is disabled. * Similarly, it_interval is set to the reset value. The function setitimer() sets the indicated timer to the * value in value. If ovalue is nonzero, the old value of the timer is stored there. */ if (setitimer(ITIMER_REAL, &interval, NULL)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed"); return -1; } getitimer(ITIMER_REAL, &interval); #endif #ifdef HAVE_FORK /* * ************************* * start watcher and workers * ************************* * * 下面程序将产生多个子进程。这些子进程成为worker,也就是用于接受处理用户的连接的进程。而当前的主进程将 * 成为watcher,主要工作就是监视workers的工作状态,当有worker因为意外而退出时,产生新的worker。 * 在程序退出时,watcher负责停止所有的workers并清理资源。 */ num_childs = srv->srvconf.max_worker;//最大worker数。 if (num_childs > 0) { int child = 0; while (!child && !srv_shutdown && !graceful_shutdown) { if (num_childs > 0) //继续产生worker { switch (fork()) { case -1: return -1; case 0: child = 1; break; default: num_childs--; break; } } else //watcher { /** * 当产生了足够的worker时,watcher就在这个while中不断的循环。 * 一但发现有worker退出(进程死亡),立即产生新的worker。 * 如果发生错误并接受到SIGHUP信号,向所有的进程(父进程及其子进程)包括自己发送SIGHUP信号。 * 并退出。 */ int status; if (-1 != wait(&status)) { /** * one of our workers went away */ num_childs++; } else { switch (errno) { case EINTR: /** * if we receive a SIGHUP we have to close our logs ourself as we don't * have the mainloop who can help us here */ if (handle_sig_hup) { handle_sig_hup = 0; log_error_cycle(srv); /** * forward to all procs in the process-group * 向所有进程发送SIGHUP信号。(父进程及其子进程) * we also send it ourself */ if (!forwarded_sig_hup) { forwarded_sig_hup = 1; kill(0, SIGHUP); } } break; default: break; } } } } /** * for the parent this is the exit-point * ***************************************************** * 父进程,也就是watcher在执行完这个if语句中就直接退出了。 * 后面是worker执行的代码。 * ***************************************************** */ if (!child) { /** * kill all children too 。杀死所有的子进程。 */ if (graceful_shutdown) { kill(0, SIGINT); } else if (srv_shutdown) { kill(0, SIGTERM); } log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; } } #endif /* * ************************** * 从这开始是worker执行的代码。 * ************************** */ if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) { log_error_write(srv, __FILE__, __LINE__, "s", "fdevent_init failed"); return -1; } /* * kqueue() is called here, select resets its internals, * all server sockets get their handlers * 将监听socket注册到fd events系统中。 * 在注册的时候为每个socket都同时注册了一个处理函数,用来处理这个socket的IO事件。 * 对于在这次调用中注册的监听socket,注册的处理函数是:network_server_handle_fdevent。 * 这个处理函数用来建立socket连接。 * */ if (0 != network_register_fdevents(srv)) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* * might fail if user is using fam (not gamin) and famd isn't running * famd没有运行,则运行失败。。。 */ if (NULL == (srv->stat_cache = stat_cache_init())) { log_error_write(srv, __FILE__, __LINE__, "s", "stat-cache could not be setup, dieing."); return -1; } #ifdef HAVE_FAM_H /* * setup FAM 设置FAM。 */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != FAMOpen2(srv->stat_cache->fam, "lighttpd")) { log_error_write(srv, __FILE__, __LINE__, "s", "could not open a fam connection, dieing."); return -1; } #ifdef HAVE_FAMNOEXISTS FAMNoExists(srv->stat_cache->fam); #endif srv->stat_cache->fam_fcce_ndx = -1; fdevent_register(srv->ev, FAMCONNECTION_GETFD(srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); fdevent_event_add(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); } #endif /* * get the current number of FDs 获得当前可用的fd值 */ srv->cur_fds = open("/dev/null", O_RDONLY); close(srv->cur_fds); for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; /* * close fd on exec (cgi) */ if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); return -1; } } /* * main-loop * ******************* * worker工作的主循环。 * ******************* */ while (!srv_shutdown) { int n; size_t ndx; time_t min_ts; /** * 收到SIGHUP信号。主要是重新开始日志的周期并提示插件。 * 这个信号表示连接已经断开,通常是做一些清理和准备工作,等待下一次的连接。 */ if (handle_sig_hup) { handler_t r; /* * reset notification 重置 */ handle_sig_hup = 0; /* * cycle logfiles * 重新开始新一轮日志。 * 这里使用了switch而不是if语句,有意思。。。 * 调用插件关于SIGHUP信号的处理函数。 * 这个函数貌似也没实现。。。 */ switch (r = plugins_call_handle_sighup(srv)) { case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "sighup-handler return with an error", r); break; } if (-1 == log_error_cycle(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "cycling errorlog failed, dying"); return -1; } else { #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "logfiles cycled UID =", last_sighup_info.si_uid, "PID =", last_sighup_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "logfiles cycled"); #endif } } /** * alarm函数发出的信号,表示一秒钟已经过去了。 */ if (handle_sig_alarm) { /* * a new second 新的一秒开始了。。。 */ #ifdef USE_ALARM /* * reset notification 重置 */ handle_sig_alarm = 0; #endif /* * get current time 当前时间。精确到一秒 */ min_ts = time(NULL); /** * 这里判断和服务器记录的当前时间是否相同。 * 相同,则表示服务器还在这一秒中,继续处理请求等。 * 如果不相同,则进入了一个新的周期(当然周期是一秒)。这就要做一些触发和检查以及清理的动作。 * 如插件的触发连接的超时清理状态缓存等。 * 其中,最主要的工作是检查连接的超时。 */ if (min_ts != srv->cur_ts) { int cs = 0; connections *conns = srv->conns; handler_t r; switch (r = plugins_call_handle_trigger(srv)) { case HANDLER_GO_ON: break; case HANDLER_ERROR: log_error_write(srv, __FILE__, __LINE__, "s", "one of the triggers failed"); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } /* * trigger waitpid 么意思?? */ srv->cur_ts = min_ts; /* * cleanup stat-cache 清理状态缓存。每秒钟清理一次。 */ stat_cache_trigger_cleanup(srv); /** * check all connections for timeouts 检查所有的连接是否超时。 */ for (ndx = 0; ndx < conns->used; ndx++) { int changed = 0; connection *con; int t_diff; con = conns->ptr[ndx]; //连接的状态是在读 if (con->state == CON_STATE_READ || con->state == CON_STATE_READ_POST) { if (con->request_count == 1) //连接正在处理一个请求 { if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { /* * time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } //这个连接同时处理多个请求 else { if (srv->cur_ts - con->read_idle_ts > con->conf.max_keep_alive_idle) { /* * time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } } //连接的状态是写 if ((con->state == CON_STATE_WRITE) && (con->write_request_ts != 0)) { #if 0 if (srv->cur_ts - con->write_request_ts > 60) { log_error_write(srv, __FILE__, __LINE__, "sdd", "connection closed - pre-write-request-timeout:", con->fd, srv->cur_ts - con->write_request_ts); } #endif if (srv->cur_ts - con->write_request_ts > con->conf.max_write_idle) { /* * time - out */ #if 1 log_error_write(srv, __FILE__, __LINE__, "sbsosds", "NOTE: a request for", con->request.uri, "timed out after writing", con->bytes_written, "bytes. We waited", (int) con->conf. max_write_idle, "seconds. If this a problem increase server.max-write-idle"); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } /* * we don't like div by zero 防止除0。。。 */ if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1; /** * 下面的if语句不是用来判断连接是否超时。 * lighttpd对每个连接设置了一个kbytes_per_second,这个变量设定每个连接在一秒钟内多能传输的最大数据量。 * 如果传送的数据大于这个值,那么这个连接将停止传输数据,被追加到作业队列中等待下一次处理。 * 作者这样做估计是为了平衡各个连接之间的数据传输。 */ if (con->traffic_limit_reached && (con->conf.kbytes_per_second == 0 || ((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))) { /* * enable connection again */ con->traffic_limit_reached = 0; changed = 1; } if (changed) { connection_state_machine(srv, con); } con->bytes_written_cur_second = 0; *(con->conf.global_bytes_per_second_cnt_ptr) = 0; #if 0 if (cs == 0) { fprintf(stderr, "connection-state: "); cs = 1; } fprintf(stderr, "c[%d,%d]: %s ", con->fd, con->fcgi.fd, connection_get_state(con->state)); #endif }//end of for( ndx = 0; ... if (cs == 1) fprintf(stderr, "\n"); }//end of if (min_ts != srv->cur_ts)... }//end of if (handle_sig_alarm)... if (srv->sockets_disabled) { /* * our server sockets are disabled, why ? * 服务器socket连接失效。为什么捏???后面的服务器过载处理中。。。 * * 将所有连接重新加入的fdevent中。 */ if ((srv->cur_fds + srv->want_fds < srv->max_fds * 0.8) && /* we have enough unused fds */ (srv->conns->used < srv->max_conns * 0.9) && (0 == graceful_shutdown)) { for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_add(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); } log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets enabled again"); srv->sockets_disabled = 0; } } else { /* * 下面处理服务器过载的情况。 */ if ((srv->cur_fds + srv->want_fds > srv->max_fds * 0.9) || /* out of fds */ (srv->conns->used > srv->max_conns) || /* out of connections */ (graceful_shutdown)) /* graceful_shutdown */ { /* * disable server-fds 关闭所有的服务socket */ for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd); if (graceful_shutdown) { /* * we don't want this socket anymore, closing it right * away will make it possible for the next lighttpd to * take over (graceful restart) */ fdevent_unregister(srv->ev, srv_socket->fd); close(srv_socket->fd); srv_socket->fd = -1; /* * network_close() will cleanup after us */ if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv -> srvconf.pid_file, errno, strerror(errno)); } } } } }//end of for(i = 0; ... if (graceful_shutdown) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] graceful shutdown started"); } else if (srv->conns->used > srv->max_conns) { log_error_write(srv, __FILE__, __LINE__, "s","[note] sockets disabled, connection limit reached"); } else { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, out-of-fds"); } srv->sockets_disabled = 1; //服务器过载了,socket失效。 } } if (graceful_shutdown && srv->conns->used == 0) { /* * we are in graceful shutdown phase and all connections are closed * we are ready to terminate without harming anyone */ srv_shutdown = 1; } /* * we still have some fds to share */ if (srv->want_fds) { /* * check the fdwaitqueue for waiting fds */ int free_fds = srv->max_fds - srv->cur_fds - 16; connection *con; for (; free_fds > 0 && NULL != (con = fdwaitqueue_unshift(srv, srv->fdwaitqueue)); free_fds--) { connection_state_machine(srv, con); srv->want_fds--; } } /** ********************************************************** * 至此,上面那些杂七杂八的事全部处理结束。下面,干正事!! * 也就是处理服务请求。 ********************************************************** */ //启动事件轮询。底层使用的是IO多路转接。 if ((n = fdevent_poll(srv->ev, 1000)) > 0) { /* * n is the number of events n是事件的数量(服务请求啦,文件读写啦什么的。。。) */ int revents; int fd_ndx; #if 0 if (n > 0) { log_error_write(srv, __FILE__, __LINE__, "sd", "polls:", n); } #endif fd_ndx = -1; /** * 这个循环中逐个的处理已经准备好的请求,知道所有的请求处理结束。 */ do { fdevent_handler handler; void *context; handler_t r; fd_ndx = fdevent_event_next_fdndx(srv->ev, fd_ndx); revents = fdevent_event_get_revent(srv->ev, fd_ndx); fd = fdevent_event_get_fd(srv->ev, fd_ndx); handler = fdevent_get_handler(srv->ev, fd); context = fdevent_get_context(srv->ev, fd); /* * connection_handle_fdevent needs a joblist_append */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sdd", "event for", fd, revents); #endif /** * 这里,调用请求的处理函数handler处理请求! * 这才是重点中的重点!! */ switch (r = (*handler) (srv, context, revents)) { case HANDLER_FINISHED: case HANDLER_GO_ON: case HANDLER_WAIT_FOR_EVENT: case HANDLER_WAIT_FOR_FD: break; case HANDLER_ERROR: /* * should never happen */ SEGFAULT(); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } }while (--n > 0); //到这里,本次的请求都处理结束了。。。累啊! } else if (n < 0 && errno != EINTR) { log_error_write(srv, __FILE__, __LINE__, "ss", "fdevent_poll failed:", strerror(errno)); } //由于上面处理的都是发生了IO事件的描述符,这些描述符的状态都被正确的设置了。 //对于没有发生IO事件的描述符,其状态机的状态也需要设置, //下面的循环就是对剩下的描述符进行处理。 for (ndx = 0; ndx < srv->joblist->used; ndx++) { connection *con = srv->joblist->ptr[ndx]; handler_t r; connection_state_machine(srv, con); switch (r = plugins_call_handle_joblist(srv, con)) { case HANDLER_FINISHED: case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } con->in_joblist = 0; } srv->joblist->used = 0; } /* end of main loop */ /* * 主循环的结束 */ if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0 && 0 == graceful_shutdown) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "server stopped by UID =", last_sigterm_info.si_uid,"PID =", last_sigterm_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "server stopped"); #endif /* * clean-up */ log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; }
int http_response_handle_cachable(server *srv, connection *con, buffer *mtime, buffer *etag) { data_string *http_if_none_match; data_string *http_if_modified_since; UNUSED(srv); /* * 14.26 If-None-Match * [...] * If none of the entity tags match, then the server MAY perform the * requested method as if the If-None-Match header field did not exist, * but MUST also ignore any If-Modified-Since header field(s) in the * request. That is, if no entity tags match, then the server MUST NOT * return a 304 (Not Modified) response. */ http_if_none_match = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("if-none-match")); http_if_modified_since = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("if-modified-since")); /* last-modified handling */ if (http_if_none_match) { if (etag_is_equal(etag, BUF_STR(http_if_none_match->value))) { if (con->request.http_method == HTTP_METHOD_GET || con->request.http_method == HTTP_METHOD_HEAD) { /* check if etag + last-modified */ if (http_if_modified_since) { size_t used_len; char *semicolon; if (NULL == (semicolon = strchr(BUF_STR(http_if_modified_since->value), ';'))) { used_len = http_if_modified_since->value->used - 1; } else { used_len = semicolon - BUF_STR(http_if_modified_since->value); } if (0 == strncmp(BUF_STR(http_if_modified_since->value), mtime->ptr, used_len)) { if ('\0' == mtime->ptr[used_len]) con->http_status = 304; return HANDLER_FINISHED; } else { #ifdef HAVE_STRPTIME char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; time_t t_header, t_file; struct tm tm; /* check if we can safely copy the string */ if (used_len >= sizeof(buf)) { TRACE("last-mod check failed as timestamp was too long: %s: %zu, %zu", SAFE_BUF_STR(http_if_modified_since->value), used_len, sizeof(buf) - 1); con->http_status = 412; return HANDLER_FINISHED; } strncpy(buf, BUF_STR(http_if_modified_since->value), used_len); buf[used_len] = '\0'; if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) { con->http_status = 412; return HANDLER_FINISHED; } tm.tm_isdst = 0; t_header = mktime(&tm); strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm); tm.tm_isdst = 0; t_file = mktime(&tm); if (t_file > t_header) return HANDLER_GO_ON; con->http_status = 304; return HANDLER_FINISHED; #else return HANDLER_GO_ON; #endif } } else { con->http_status = 304; return HANDLER_FINISHED; } } else { con->http_status = 412; return HANDLER_FINISHED; } } } else if (http_if_modified_since) { size_t used_len; char *semicolon; if (NULL == (semicolon = strchr(BUF_STR(http_if_modified_since->value), ';'))) { used_len = http_if_modified_since->value->used - 1; } else { used_len = semicolon - BUF_STR(http_if_modified_since->value); } if (0 == strncmp(BUF_STR(http_if_modified_since->value), mtime->ptr, used_len)) { if ('\0' == mtime->ptr[used_len]) con->http_status = 304; return HANDLER_FINISHED; } else { #ifdef HAVE_STRPTIME char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; time_t t_header, t_file; struct tm tm; /* convert to timestamp */ if (used_len >= sizeof(buf)) return HANDLER_GO_ON; strncpy(buf, BUF_STR(http_if_modified_since->value), used_len); buf[used_len] = '\0'; if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) { return HANDLER_GO_ON; } tm.tm_isdst = 0; t_header = mktime(&tm); strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm); tm.tm_isdst = 0; t_file = mktime(&tm); if (t_file > t_header) return HANDLER_GO_ON; con->http_status = 304; return HANDLER_FINISHED; #else return HANDLER_GO_ON; #endif } } return HANDLER_GO_ON; }
/* 0: everything ok, -1: error, -2: con closed */ static int connection_handle_read(server *srv, connection *con) { int len; buffer *b; int toread, read_offset; if (con->conf.is_ssl) { return connection_handle_read_ssl(srv, con); } b = (NULL != con->read_queue->last) ? con->read_queue->last->mem : NULL; /* default size for chunks is 4kb; only use bigger chunks if FIONREAD tells * us more than 4kb is available * if FIONREAD doesn't signal a big chunk we fill the previous buffer * if it has >= 1kb free */ #if defined(__WIN32) if (NULL == b || b->size - b->used < 1024) { b = chunkqueue_get_append_buffer(con->read_queue); buffer_prepare_copy(b, 4 * 1024); } read_offset = (b->used == 0) ? 0 : b->used - 1; len = recv(con->fd, b->ptr + read_offset, b->size - 1 - read_offset, 0); #else #ifdef HAVE_LIBMTCP /* toread = MAX_READ_LIMIT; */ if (mtcp_socket_ioctl(srv->mctx, con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { #else if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) { #endif if (NULL == b || b->size - b->used < 1024) { b = chunkqueue_get_append_buffer(con->read_queue); buffer_prepare_copy(b, 4 * 1024); } } else { if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT; b = chunkqueue_get_append_buffer(con->read_queue); buffer_prepare_copy(b, toread + 1); } read_offset = (b->used == 0) ? 0 : b->used - 1; #ifdef HAVE_LIBMTCP len = mtcp_read(srv->mctx, con->fd, b->ptr + read_offset, b->size - 1 - read_offset); #else len = read(con->fd, b->ptr + read_offset, b->size - 1 - read_offset); #endif #endif if (len < 0) { con->is_readable = 0; if (errno == EAGAIN) return 0; if (errno == EINTR) { /* we have been interrupted before we could read */ con->is_readable = 1; return 0; } if (errno != ECONNRESET) { /* expected for keep-alive */ log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); } connection_set_state(srv, con, CON_STATE_ERROR); return -1; } else if (len == 0) { con->is_readable = 0; /* the other end close the connection -> KEEP-ALIVE */ /* pipelining */ return -2; } else if ((size_t)len < b->size - 1) { /* we got less then expected, wait for the next fd-event */ con->is_readable = 0; } if (b->used > 0) b->used--; b->used += len; b->ptr[b->used++] = '\0'; con->bytes_read += len; #if 0 dump_packet(b->ptr, len); #endif return 0; } 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: case HTTP_METHOD_PUT: case HTTP_METHOD_PATCH: case HTTP_METHOD_MKCOL: case HTTP_METHOD_DELETE: case HTTP_METHOD_COPY: case HTTP_METHOD_MOVE: case HTTP_METHOD_PROPFIND: case HTTP_METHOD_PROPPATCH: case HTTP_METHOD_LOCK: case HTTP_METHOD_UNLOCK: 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) && con->uri.path->used && 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: switch(con->http_status) { case 400: /* bad request */ case 401: /* authorization required */ case 414: /* overload request header */ case 505: /* unknown protocol */ case 207: /* this was webdav */ break; default: con->http_status = 501; break; } 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; buffer_reset(con->physical.path); /* try to send static errorfile */ if (!buffer_is_empty(con->conf.errorfile_prefix)) { stat_cache_entry *sce = NULL; buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix); buffer_append_long(con->physical.path, con->http_status); buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { con->file_finished = 1; http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size); 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 = chunkqueue_get_append_buffer(con->write_queue); /* 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_long(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_long(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" )); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } break; } 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_off_t(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)) { con->keep_alive = 0; } /** * if the backend sent a Connection: close, follow the wish * * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we * will close the connection. That's fine. We can always decide the close * the connection * * FIXME: to be nice we should remove the Connection: ... */ if (con->parsed_response & HTTP_CONNECTION) { /* a subrequest disable keep-alive although the client wanted it */ if (con->keep_alive && !con->response.keep_alive) { con->keep_alive = 0; } } } if (con->request.http_method == HTTP_METHOD_HEAD) { /** * a HEAD request has the same as a GET * without the content */ con->file_finished = 1; chunkqueue_reset(con->write_queue); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; } http_response_write_header(srv, con); return 0; } static int connection_handle_write(server *srv, connection *con) { switch(network_write_chunkqueue(srv, con, con->write_queue, MAX_WRITE_LIMIT)) { case 0: con->write_request_ts = srv->cur_ts; if (con->file_finished) { connection_set_state(srv, con, CON_STATE_RESPONSE_END); joblist_append(srv, con); } break; case -1: /* error on our side */ log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed: write failed on fd", con->fd); connection_set_state(srv, con, CON_STATE_ERROR); joblist_append(srv, con); break; case -2: /* remote close */ connection_set_state(srv, con, CON_STATE_ERROR); joblist_append(srv, con); break; case 1: con->write_request_ts = srv->cur_ts; con->is_writable = 0; /* not finished yet -> WRITE */ break; } return 0; } connection *connection_init(server *srv) { connection *con; UNUSED(srv); con = calloc(1, sizeof(*con)); con->fd = 0; con->ndx = -1; con->fde_ndx = -1; con->bytes_written = 0; con->bytes_read = 0; con->bytes_header = 0; con->loops_per_request = 0; #define CLEAN(x) \ con->x = buffer_init(); CLEAN(request.uri); CLEAN(request.request_line); CLEAN(request.request); CLEAN(request.pathinfo); CLEAN(request.orig_uri); CLEAN(uri.scheme); CLEAN(uri.authority); CLEAN(uri.path); CLEAN(uri.path_raw); CLEAN(uri.query); CLEAN(physical.doc_root); CLEAN(physical.path); CLEAN(physical.basedir); CLEAN(physical.rel_path); CLEAN(physical.etag); CLEAN(parse_request); CLEAN(authed_user); CLEAN(server_name); CLEAN(error_handler); CLEAN(dst_addr_buf); #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT CLEAN(tlsext_server_name); #endif #undef CLEAN con->write_queue = chunkqueue_init(); con->read_queue = chunkqueue_init(); con->request_content_queue = chunkqueue_init(); chunkqueue_set_tempdirs(con->request_content_queue, srv->srvconf.upload_tempdirs); con->request.headers = array_init(); con->response.headers = array_init(); con->environment = array_init(); /* init plugin specific connection structures */ con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *)); con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); config_setup_connection(srv, con); return con; }
void http_response_send_file (server *srv, connection *con, buffer *path) { stat_cache_entry *sce = NULL; buffer *mtime = NULL; data_string *ds; int allow_caching = (0 == con->http_status || 200 == con->http_status); if (HANDLER_ERROR == stat_cache_get_entry(srv, con, path, &sce)) { con->http_status = (errno == ENOENT) ? 404 : 403; log_error_write(srv, __FILE__, __LINE__, "sbsb", "not a regular file:", con->uri.path, "->", path); return; } /* we only handline regular files */ #ifdef HAVE_LSTAT if ((sce->is_symlink == 1) && !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 :", path); } return; } #endif if (!S_ISREG(sce->st.st_mode)) { con->http_status = 403; if (con->conf.log_file_not_found) { log_error_write(srv, __FILE__, __LINE__, "sbsb", "not a regular file:", con->uri.path, "->", sce->name); } return; } /* mod_compress might set several data directly, don't overwrite them */ /* set response content-type, if not set already */ if (NULL == array_get_element(con->response.headers, "Content-Type")) { if (buffer_string_is_empty(sce->content_type)) { /* we are setting application/octet-stream, but also announce that * this header field might change in the seconds few requests * * This should fix the aggressive caching of FF and the script download * seen by the first installations */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream")); allow_caching = 0; } else { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); } } if (con->conf.range_requests) { response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes")); } if (allow_caching) { if (con->etag_flags != 0 && !buffer_string_is_empty(sce->etag)) { if (NULL == array_get_element(con->response.headers, "ETag")) { /* generate e-tag */ etag_mutate(con->physical.etag, sce->etag); response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); } } /* prepare header */ if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) { mtime = strftime_cache_get(srv, sce->st.st_mtime); response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); } else { mtime = ds->value; } if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { return; } } if (con->request.http_range && con->conf.range_requests && (200 == con->http_status || 0 == con->http_status) && NULL == array_get_element(con->response.headers, "Content-Encoding")) { int do_range_request = 1; /* check if we have a conditional GET */ if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) { /* if the value is the same as our ETag, we do a Range-request, * otherwise a full 200 */ if (ds->value->ptr[0] == '"') { /** * client wants a ETag */ if (!con->physical.etag) { do_range_request = 0; } else if (!buffer_is_equal(ds->value, con->physical.etag)) { do_range_request = 0; } } else if (!mtime) { /** * we don't have a Last-Modified and can match the If-Range: * * sending all */ do_range_request = 0; } else if (!buffer_is_equal(ds->value, mtime)) { do_range_request = 0; } } if (do_range_request) { /* content prepared, I'm done */ con->file_finished = 1; if (0 == http_response_parse_range(srv, con, path, sce)) { con->http_status = 206; } return; } } /* if we are still here, prepare body */ /* we add it here for all requests * the HEAD request will drop it afterwards again */ if (0 == sce->st.st_size || 0 == http_chunk_append_file(srv, con, path)) { con->http_status = 200; con->file_finished = 1; } else { con->http_status = 403; } }
//该函数用于完成用户配置到程序变量的转换 int config_insert_values_internal(server *srv, array *ca, const config_values_t cv[]) { size_t i; data_unset *du; for (i = 0; cv[i].key; i++) { //根据key获取配置项 if (NULL == (du = array_get_element(ca, cv[i].key))) { //从ca->data里获取key=cv[i].key的元素放入du /* no found */ //没有找到对应的配置项,即使用默认配置值,继续下一个处理 continue; } switch (cv[i].type) { case T_CONFIG_ARRAY: /* 数组类型的配置信息有: server.modules={"mod_indexfiles" "mod_dirlisting" "mod_staticfile" "mod_access" "mod_auth" "mod_accesslog"} */ if (du->type == TYPE_ARRAY) { //数组类型的配置信息 size_t j; data_array *da = (data_array *)du; //将du的值赋给da for (j = 0; j < da->value->used; j++) { //必定是string类型 if (da->value->data[j]->type == TYPE_STRING) { data_string *ds = data_string_init(); buffer_copy_string_buffer(ds->value, ((data_string *)(da->value->data[j]))->value); //将da->value->data[j]->value复制到ds->value if (!da->is_index_key) { /* the id's were generated automaticly, as we copy now we might have to renumber them * this is used to prepend server.modules by mod_indexfiles as it has to be loaded * before mod_fastcgi and friends */ buffer_copy_string_buffer(ds->key, ((data_string *)(da->value->data[j]))->key); } array_insert_unique(cv[i].destination, (data_unset *)ds); } else { log_error_write(srv, __FILE__, __LINE__, "sssd", "the key of an array can only be a string or a integer, variable:", cv[i].key, "type:", da->value->data[j]->type); return -1; } } } else { log_error_write(srv, __FILE__, __LINE__, "ss", cv[i].key, "should have been a array of strings like ... = ( \"...\" )"); return -1; } break; case T_CONFIG_STRING://字符串类型 /* 字符串类型配置信息: server.document-root="/home/lenky/source/lighttpd-1.4.20/lenky/" */ if (du->type == TYPE_STRING) { data_string *ds = (data_string *)du; buffer_copy_string_buffer(cv[i].destination, ds->value); } else { log_error_write(srv, __FILE__, __LINE__, "ssss", cv[i].key, "should have been a string like ... = \"...\""); return -1; } break; case T_CONFIG_SHORT: //短整型类型的配置信息 /* 短整型的配置信息: server.port=3000 server.max-worker=4 server.max-fds=800 */ switch(du->type) { case TYPE_INTEGER: { data_integer *di = (data_integer *)du; *((unsigned short *)(cv[i].destination)) = di->value; break; } case TYPE_STRING: { data_string *ds = (data_string *)du; log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected a short:", cv[i].key, ds->value); return -1; } default: log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected a integer, range 0 ... 65535"); return -1; } break; case T_CONFIG_BOOLEAN: //布尔类型配置信息 /* 布尔型的配置信息: dir-listing.sctivate="enable" $HTTP["url"]=~"^/www/"{ dir-listing.sctivate="disable" */ if (du->type == TYPE_STRING) { data_string *ds = (data_string *)du; if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) { *((unsigned short *)(cv[i].destination)) = 1; } else if (buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) { *((unsigned short *)(cv[i].destination)) = 0; } else { log_error_write(srv, __FILE__, __LINE__, "ssbs", "ERROR: unexpected value for key:", cv[i].key, ds->value, "(enable|disable)"); return -1; } } else { log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: unexpected type for key:", cv[i].key, "(string)", "\"(enable|disable)\""); return -1; } break; case T_CONFIG_LOCAL: //本地类型和位置类型不获取值 case T_CONFIG_UNSET: break; case T_CONFIG_UNSUPPORTED: //不支持类型 log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found unsupported key:", cv[i].key, "-", (char *)(cv[i].destination)); srv->config_unsupported = 1; break; case T_CONFIG_DEPRECATED: //已被摒弃 log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found deprecated key:", cv[i].key, "-", (char *)(cv[i].destination)); srv->config_deprecated = 1; break; } } return 0; }
static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) { buffer *l; server_socket *srv_sock = con->srv_socket; //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 */ //在判断前驱块状态时候有可能就已经设置了本快的状态(config_check_cond_cached函数调用如前驱块为真,该前驱块以下的块将全都设置为假),如果为假则直接返回。 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 */ //开始实际的连接状态判断,Lighttpd1.4.20提供的条件配置有10个,分别为server_socket HTTP_URL HTTP_HOST HTTP_REFERER HTTP_USER_AGENT HTTP_COOKIE //HTTP_REMOTE_IP HTTP_QUERY_STRING HTTP_SCHEME HTTP_REQUEST_METHOD switch (dc->comp) { case COMP_HTTP_HOST: { char *ck_colon = NULL, *val_colon = NULL; if (!buffer_is_empty(con->uri.authority)) { //authority内保存是请求连接的Host信息(可能是域名也可能是IP地址) /* * 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 (ck_colon == val_colon) { //请求连接的Host信息与条件配置块的Host条件设置格式一致(即两者都包含有端口号或都没有包含端口号),则什么都不做。 /* nothing to do with it */ break; } if (ck_colon) { //请求连接的Host信息没有半酣端口号而条件配置块的Host包含端口号,因此给请求连接的Host加上端口号 /* condition "host:port" but client send "host" */ buffer_copy_string_buffer(srv->cond_check_buf, l); buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":")); buffer_append_long(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr))); l = srv->cond_check_buf; } else if (!ck_colon) { //请求连接的Host信息包含端口号而条件配置信息块的Host没有包含端口号,因此将请求连接Host的端口号去掉。 /* 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; } } else { l = srv->empty_string; } break; } case COMP_HTTP_REMOTE_IP: { //REMOTE adj 遥远的 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') { //无分类域间路由选择CIDR(CIDR记法,斜线记法),这里对CIDR格式字符串进行检验 log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string); //CIDR格式不对,缺少表示网络前缀位数的数字 return COND_RESULT_FALSE; } /* 函数strtol()声明在头文件stdlib.h内,原型为long int strtol(const char *nptr,char **endptr,int base);用于将参数nptr字符串根据 base指定的进制转换成对应的长整型数。参数base范围从2至36,或0(即默认采用十进制做转换,但遇到如'0x'前置字符则会使用十六进制做转换)。 strtol()会扫描参数nptr字符串,跳过前面的空格字符,知道遇上数字或正负号才开始做转换,在遇到非数字或字符串结束时('\0')结束转换,并将结果返回。 若参数endptr不为NULL,则会将不符合调节而终止的nptr中的字符指针由endptr返回。该函数执行成功返回转换后的长整型数,否则返回ERANGE(表示指定的专函字符串超出合法范围) 并将错误代码存入errno中,此处用于获取端口十进制的整型数。 */ 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; } /* 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 /* 函数inet_ston()声明在头文件sys/scoket.h内,原型为int inet_aton(const char *cp,struct in_addr *inp); 用于将参数cp所指的字符串形式的网络地址 转换成网络地址成网络使用的二进制数形式,然后存于参数inp所指的in_addr结构中。 */ 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 */ /* 函数htonl()声明在头文件srpa/inet.h内,原型为unint32_t htonl(uint32_t hostlong); 用来将参数hostlong指定的32位无符号长整型由主机字节顺序转换成网络字符顺序。 */ nm = htonl(~((1 << (32 - nm_bits)) - 1)); if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) { //当前连接的客户端IP地址与条件配置信息块的条件设置匹配,按需返回结果 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: { /* get_http_method_name()函数根据当前连接的请求方法(通过分析请求行得知)返回对应的字符串,比如"GET"、"POST"等 */ 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结构体。 buffer_copy_string(srv->tmp_buf, method); l = srv->tmp_buf; 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 /* 正则式匹配需要相应库的支持,GNU/Linux下有两套正则式编程支持库:POSIX库和PCRE库,POSIX库不需要单独安装,能满足一般需求,但是速度稍慢些, 读者查看MAN手册。PCRE库久负盛名,功能强大,匹配速度快,但是可能需要单独安装。关于PCRE库的更多介绍,读者可以查阅站点:http://www.pcre.org/。 此处用的是PCRE库。 */ 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, l->ptr, l->used - 1, 0, 0, cache->matches, elementsof(cache->matches)); //利用PCRE库函数pcre_exec()执行匹配操作,如果不匹配或执行出错则返回一个负值(其中,不匹配则返回PCRE_ERROR_NOMATCH(该宏值为-1)), //如果匹配成功将返回一个正数。关于函数pcre_exec()的详细说明可以参考说明文档:http://www.pcre.org/prce.txt. 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; }
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; /* use the current dir as basedir for all other includes */ pos = strrchr(fn, DIR_SEPERATOR); 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, CONST_STR_LEN("global")))) { srv->config = dc->value; } else { return -1; } if (NULL != (modules = (data_array *)array_get_element(srv->config, CONST_STR_LEN("server.modules")))) { data_string *ds; data_array *prepends; int prepend_mod_indexfile = 1; int append_mod_dirlisting = 1; int append_mod_staticfile = 1; int append_mod_chunked = 1; size_t i; if (modules->type != TYPE_ARRAY) { fprintf(stderr, "server.modules must be an array"); return -1; } prepends = data_array_init(); /* prepend default modules */ for (i = 0; i < modules->value->used; i++) { ds = (data_string *)modules->value->data[i]; if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_indexfile"))) { prepend_mod_indexfile = 0; } if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_staticfile"))) { append_mod_staticfile = 0; } if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_dirlisting"))) { append_mod_dirlisting = 0; } if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_chunked"))) { append_mod_chunked = 0; } if (0 == prepend_mod_indexfile && 0 == append_mod_dirlisting && 0 == append_mod_staticfile && 0 == append_mod_chunked) { break; } } if (prepend_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 (append_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 (append_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); } if (append_mod_chunked) { ds = data_string_init(); buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_chunked")); 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); ds = data_string_init(); buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_chunked")); 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; }
static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, const char **l, size_t n, stat_cache_entry *sce) { size_t i, ssicmd = 0; char buf[255]; buffer *b = NULL; struct { const char *var; enum { SSI_UNSET, SSI_ECHO, SSI_FSIZE, SSI_INCLUDE, SSI_FLASTMOD, SSI_CONFIG, SSI_PRINTENV, SSI_SET, SSI_IF, SSI_ELIF, SSI_ELSE, SSI_ENDIF, SSI_EXEC } type; } ssicmds[] = { { "echo", SSI_ECHO }, { "include", SSI_INCLUDE }, { "flastmod", SSI_FLASTMOD }, { "fsize", SSI_FSIZE }, { "config", SSI_CONFIG }, { "printenv", SSI_PRINTENV }, { "set", SSI_SET }, { "if", SSI_IF }, { "elif", SSI_ELIF }, { "endif", SSI_ENDIF }, { "else", SSI_ELSE }, { "exec", SSI_EXEC }, { NULL, SSI_UNSET } }; for (i = 0; ssicmds[i].var; i++) { if (0 == strcmp(l[1], ssicmds[i].var)) { ssicmd = ssicmds[i].type; break; } } switch(ssicmd) { case SSI_ECHO: { /* echo */ int var = 0; /* int enc = 0; */ const char *var_val = NULL; struct { const char *var; enum { SSI_ECHO_UNSET, SSI_ECHO_DATE_GMT, SSI_ECHO_DATE_LOCAL, SSI_ECHO_DOCUMENT_NAME, SSI_ECHO_DOCUMENT_URI, SSI_ECHO_LAST_MODIFIED, SSI_ECHO_USER_NAME } type; } echovars[] = { { "DATE_GMT", SSI_ECHO_DATE_GMT }, { "DATE_LOCAL", SSI_ECHO_DATE_LOCAL }, { "DOCUMENT_NAME", SSI_ECHO_DOCUMENT_NAME }, { "DOCUMENT_URI", SSI_ECHO_DOCUMENT_URI }, { "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED }, { "USER_NAME", SSI_ECHO_USER_NAME }, { NULL, SSI_ECHO_UNSET } }; /* struct { const char *var; enum { SSI_ENC_UNSET, SSI_ENC_URL, SSI_ENC_NONE, SSI_ENC_ENTITY } type; } encvars[] = { { "url", SSI_ENC_URL }, { "none", SSI_ENC_NONE }, { "entity", SSI_ENC_ENTITY }, { NULL, SSI_ENC_UNSET } }; */ for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "var")) { int j; var_val = l[i+1]; for (j = 0; echovars[j].var; j++) { if (0 == strcmp(l[i+1], echovars[j].var)) { var = echovars[j].type; break; } } } else if (0 == strcmp(l[i], "encoding")) { /* int j; for (j = 0; encvars[j].var; j++) { if (0 == strcmp(l[i+1], encvars[j].var)) { enc = encvars[j].type; break; } } */ } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (p->if_is_false) break; if (!var_val) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "var is missing"); break; } switch(var) { case SSI_ECHO_USER_NAME: { struct passwd *pw; b = chunkqueue_get_append_buffer(con->write_queue); #ifdef HAVE_PWD_H if (NULL == (pw = getpwuid(sce->st.st_uid))) { buffer_copy_long(b, sce->st.st_uid); } else { buffer_copy_string(b, pw->pw_name); } #else buffer_copy_long(b, sce->st.st_uid); #endif break; } case SSI_ECHO_LAST_MODIFIED: { time_t t = sce->st.st_mtime; b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } break; } case SSI_ECHO_DATE_LOCAL: { time_t t = time(NULL); b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } break; } case SSI_ECHO_DATE_GMT: { time_t t = time(NULL); b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } break; } case SSI_ECHO_DOCUMENT_NAME: { char *sl; b = chunkqueue_get_append_buffer(con->write_queue); if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { buffer_copy_string_buffer(b, con->physical.path); } else { buffer_copy_string(b, sl + 1); } break; } case SSI_ECHO_DOCUMENT_URI: { b = chunkqueue_get_append_buffer(con->write_queue); buffer_copy_string_buffer(b, con->uri.path); break; } default: { data_string *ds; /* check if it is a cgi-var */ b = chunkqueue_get_append_buffer(con->write_queue); if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, var_val))) { buffer_copy_string_buffer(b, ds->value); } else { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } break; } } break; } case SSI_INCLUDE: case SSI_FLASTMOD: case SSI_FSIZE: { const char * file_path = NULL, *virt_path = NULL; struct stat st; char *sl; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "file")) { file_path = l[i+1]; } else if (0 == strcmp(l[i], "virtual")) { virt_path = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (!file_path && !virt_path) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "file or virtual are missing"); break; } if (file_path && virt_path) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "only one of file and virtual is allowed here"); break; } if (p->if_is_false) break; if (file_path) { /* current doc-root */ if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { buffer_copy_string_len(p->stat_fn, CONST_STR_LEN("/")); } else { buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, sl - con->physical.path->ptr + 1); } buffer_copy_string(srv->tmp_buf, file_path); buffer_urldecode_path(srv->tmp_buf); buffer_path_simplify(srv->tmp_buf, srv->tmp_buf); buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); } else { /* virtual */ if (virt_path[0] == '/') { buffer_copy_string(p->stat_fn, virt_path); } else { /* there is always a / */ sl = strrchr(con->uri.path->ptr, '/'); buffer_copy_string_len(p->stat_fn, con->uri.path->ptr, sl - con->uri.path->ptr + 1); buffer_append_string(p->stat_fn, virt_path); } buffer_urldecode_path(p->stat_fn); buffer_path_simplify(srv->tmp_buf, p->stat_fn); /* we have an uri */ buffer_copy_string_buffer(p->stat_fn, con->physical.doc_root); buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); } if (0 == stat(p->stat_fn->ptr, &st)) { time_t t = st.st_mtime; switch (ssicmd) { case SSI_FSIZE: b = chunkqueue_get_append_buffer(con->write_queue); if (p->sizefmt) { int j = 0; const char *abr[] = { " B", " kB", " MB", " GB", " TB", NULL }; off_t s = st.st_size; for (j = 0; s > 1024 && abr[j+1]; s /= 1024, j++); buffer_copy_off_t(b, s); buffer_append_string(b, abr[j]); } else { buffer_copy_off_t(b, st.st_size); } break; case SSI_FLASTMOD: b = chunkqueue_get_append_buffer(con->write_queue); if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { buffer_copy_string_len(b, CONST_STR_LEN("(none)")); } else { buffer_copy_string(b, buf); } break; case SSI_INCLUDE: chunkqueue_append_file(con->write_queue, p->stat_fn, 0, st.st_size); /* Keep the newest mtime of included files */ if (st.st_mtime > include_file_last_mtime) include_file_last_mtime = st.st_mtime; break; } } else { log_error_write(srv, __FILE__, __LINE__, "sbs", "ssi: stating failed ", p->stat_fn, strerror(errno)); } break; } case SSI_SET: { const char *key = NULL, *val = NULL; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "var")) { key = l[i+1]; } else if (0 == strcmp(l[i], "value")) { val = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (p->if_is_false) break; if (key && val) { data_string *ds; if (NULL == (ds = (data_string *)array_get_unused_element(p->ssi_vars, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string(ds->key, key); buffer_copy_string(ds->value, val); array_insert_unique(p->ssi_vars, (data_unset *)ds); } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: var and value have to be set in", l[0], l[1]); } break; } case SSI_CONFIG: if (p->if_is_false) break; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "timefmt")) { buffer_copy_string(p->timefmt, l[i+1]); } else if (0 == strcmp(l[i], "sizefmt")) { if (0 == strcmp(l[i+1], "abbrev")) { p->sizefmt = 1; } else if (0 == strcmp(l[i+1], "abbrev")) { p->sizefmt = 0; } else { log_error_write(srv, __FILE__, __LINE__, "sssss", "ssi: unknow value for attribute '", l[i], "' for ", l[1], l[i+1]); } } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } break; case SSI_PRINTENV: if (p->if_is_false) break; b = chunkqueue_get_append_buffer(con->write_queue); for (i = 0; i < p->ssi_vars->used; i++) { data_string *ds = (data_string *)p->ssi_vars->data[p->ssi_vars->sorted[i]]; buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN("=")); buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); buffer_append_string_len(b, CONST_STR_LEN("\n")); } for (i = 0; i < p->ssi_cgi_env->used; i++) { data_string *ds = (data_string *)p->ssi_cgi_env->data[p->ssi_cgi_env->sorted[i]]; buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN("=")); buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); buffer_append_string_len(b, CONST_STR_LEN("\n")); } break; case SSI_EXEC: { const char *cmd = NULL; pid_t pid; int from_exec_fds[2]; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "cmd")) { cmd = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (p->if_is_false) break; /* create a return pipe and send output to the html-page * * as exec is assumed evil it is implemented synchronously */ if (!cmd) break; #ifdef HAVE_FORK if (pipe(from_exec_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); return -1; } /* fork, execve */ switch (pid = fork()) { case 0: { /* move stdout to from_rrdtool_fd[1] */ close(STDOUT_FILENO); dup2(from_exec_fds[1], STDOUT_FILENO); close(from_exec_fds[1]); /* not needed */ close(from_exec_fds[0]); /* close stdin */ close(STDIN_FILENO); execl("/bin/sh", "sh", "-c", cmd, (char *)NULL); log_error_write(srv, __FILE__, __LINE__, "sss", "spawing exec failed:", strerror(errno), cmd); /* */ SEGFAULT(); break; } case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); break; default: { /* father */ int status; ssize_t r; int was_interrupted = 0; close(from_exec_fds[1]); /* wait for the client to end */ /* * OpenBSD and Solaris send a EINTR on SIGCHILD even if we ignore it */ do { if (-1 == waitpid(pid, &status, 0)) { if (errno == EINTR) { was_interrupted++; } else { was_interrupted = 0; log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno)); } } else if (WIFEXITED(status)) { int toread; /* read everything from client and paste it into the output */ was_interrupted = 0; while(1) { if (ioctl(from_exec_fds[0], FIONREAD, &toread)) { log_error_write(srv, __FILE__, __LINE__, "s", "unexpected end-of-file (perhaps the ssi-exec process died)"); return -1; } if (toread > 0) { b = chunkqueue_get_append_buffer(con->write_queue); buffer_prepare_copy(b, toread + 1); if ((r = read(from_exec_fds[0], b->ptr, b->size - 1)) < 0) { /* read failed */ break; } else { b->used = r; b->ptr[b->used++] = '\0'; } } else { break; } } } else { was_interrupted = 0; log_error_write(srv, __FILE__, __LINE__, "s", "process exited abnormally"); } } while (was_interrupted > 0 && was_interrupted < 4); /* if waitpid() gets interrupted, retry, but max 4 times */ close(from_exec_fds[0]); break; } } #else return -1; #endif break; } case SSI_IF: { const char *expr = NULL; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "expr")) { expr = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (!expr) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "expr missing"); break; } if ((!p->if_is_false) && ((p->if_is_false_level == 0) || (p->if_level < p->if_is_false_level))) { switch (ssi_eval_expr(srv, con, p, expr)) { case -1: case 0: p->if_is_false = 1; p->if_is_false_level = p->if_level; break; case 1: p->if_is_false = 0; break; } } p->if_level++; break; } case SSI_ELSE: p->if_level--; if (p->if_is_false) { if ((p->if_level == p->if_is_false_level) && (p->if_is_false_endif == 0)) { p->if_is_false = 0; } } else { p->if_is_false = 1; p->if_is_false_level = p->if_level; } p->if_level++; break; case SSI_ELIF: { const char *expr = NULL; for (i = 2; i < n; i += 2) { if (0 == strcmp(l[i], "expr")) { expr = l[i+1]; } else { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: unknow attribute for ", l[1], l[i]); } } if (!expr) { log_error_write(srv, __FILE__, __LINE__, "sss", "ssi: ", l[1], "expr missing"); break; } p->if_level--; if (p->if_level == p->if_is_false_level) { if ((p->if_is_false) && (p->if_is_false_endif == 0)) { switch (ssi_eval_expr(srv, con, p, expr)) { case -1: case 0: p->if_is_false = 1; p->if_is_false_level = p->if_level; break; case 1: p->if_is_false = 0; break; } } else { p->if_is_false = 1; p->if_is_false_level = p->if_level; p->if_is_false_endif = 1; } } p->if_level++; break; } case SSI_ENDIF: p->if_level--; if (p->if_level == p->if_is_false_level) { p->if_is_false = 0; p->if_is_false_endif = 0; } break; default: log_error_write(srv, __FILE__, __LINE__, "ss", "ssi: unknow ssi-command:", l[1]); break; } return 0; }
/** parse config array */ static int config_insert_values_internal(server *srv, array *ca, const config_values_t cv[]) { size_t i; data_unset *du; for (i = 0; cv[i].key; i++) { if (NULL == (du = array_get_element(ca, cv[i].key))) { /* no found */ continue; } switch (cv[i].type) { case T_CONFIG_ARRAY: if (du->type == TYPE_ARRAY) { size_t j; data_array *da = (data_array *)du; for (j = 0; j < da->value->used; j++) { if (da->value->data[j]->type == TYPE_STRING) { data_string *ds = data_string_init(); buffer_copy_string_buffer(ds->value, ((data_string *)(da->value->data[j]))->value); if (!da->is_index_key) { /* the id's were generated automaticly, as we copy now we might have to renumber them * this is used to prepend server.modules by mod_indexfiles as it has to be loaded * before mod_fastcgi and friends */ buffer_copy_string_buffer(ds->key, ((data_string *)(da->value->data[j]))->key); } array_insert_unique(cv[i].destination, (data_unset *)ds); } else { log_error_write(srv, __FILE__, __LINE__, "sssd", "the key of an array can only be a string or a integer, variable:", cv[i].key, "type:", da->value->data[j]->type); return -1; } } } else { log_error_write(srv, __FILE__, __LINE__, "ss", cv[i].key, "should have been a array of strings like ... = ( \"...\" )"); return -1; } break; case T_CONFIG_STRING: if (du->type == TYPE_STRING) { data_string *ds = (data_string *)du; buffer_copy_string_buffer(cv[i].destination, ds->value); } else { log_error_write(srv, __FILE__, __LINE__, "ssss", cv[i].key, "should have been a string like ... = \"...\""); return -1; } break; case T_CONFIG_SHORT: switch(du->type) { case TYPE_INTEGER: { data_integer *di = (data_integer *)du; *((unsigned short *)(cv[i].destination)) = di->value; break; } case TYPE_STRING: { //data_string *ds = (data_string *)du; //log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected a short:", cv[i].key, ds->value); return -1; } default: log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected a integer, range 0 ... 65535"); return -1; } break; case T_CONFIG_BOOLEAN: if (du->type == TYPE_STRING) { data_string *ds = (data_string *)du; if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) { *((unsigned short *)(cv[i].destination)) = 1; } else if (buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) { *((unsigned short *)(cv[i].destination)) = 0; } else { log_error_write(srv, __FILE__, __LINE__, "ssbs", "ERROR: unexpected value for key:", cv[i].key, ds->value, "(enable|disable)"); return -1; } } else { log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: unexpected type for key:", cv[i].key, "(string)", "\"(enable|disable)\""); return -1; } break; case T_CONFIG_LOCAL: case T_CONFIG_UNSET: break; case T_CONFIG_UNSUPPORTED: log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found unsupported key:", cv[i].key, "-", (char *)(cv[i].destination)); // srv->config_unsupported = 1; break; case T_CONFIG_DEPRECATED: log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found deprecated key:", cv[i].key, "-", (char *)(cv[i].destination)); // srv->config_deprecated = 1; break; } } return 0; }
static int http_response_parse_range(server *srv, connection *con, plugin_data *p) { int multipart = 0; char *boundary = "fkj49sn38dcn3"; data_string *ds; stat_cache_entry *sce = NULL; buffer *content_type = NULL; buffer *range = NULL; http_req_range *ranges, *r; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Range")))) { range = ds->value; } else { /* we don't have a Range header */ return -1; } if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { SEGFAULT("stat_cache_get_entry(%s) returned %d", SAFE_BUF_STR(con->physical.path), HANDLER_ERROR); } con->response.content_length = 0; if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Content-Type")))) { content_type = ds->value; } /* start the range-header parser * bytes=<num> */ ranges = p->ranges; http_request_range_reset(ranges); switch (http_request_range_parse(range, ranges)) { case PARSE_ERROR: return -1; /* no range valid Range Header */ case PARSE_SUCCESS: break; default: TRACE("%s", "foobar"); return -1; } if (ranges->next) { multipart = 1; } /* patch the '-1' */ for (r = ranges; r; r = r->next) { if (r->start == -1) { /* -<end> * * the last <end> bytes */ r->start = sce->st.st_size - r->end; r->end = sce->st.st_size - 1; } if (r->end == -1) { /* <start>- * all but the first <start> bytes */ r->end = sce->st.st_size - 1; } if (r->end > sce->st.st_size - 1) { /* RFC 2616 - 14.35.1 * * if last-byte-pos not present or > size-of-file * take the size-of-file * * */ r->end = sce->st.st_size - 1; } if (r->start > sce->st.st_size - 1) { /* RFC 2616 - 14.35.1 * * if first-byte-pos > file-size, 416 */ con->http_status = 416; return -1; } if (r->start > r->end) { /* RFC 2616 - 14.35.1 * * if last-byte-pos is present, it has to be >= first-byte-pos * * invalid ranges have to be handle as no Range specified * */ return -1; } } if (r) { /* we ran into an range violation */ return -1; } if (multipart) { buffer *b; for (r = ranges; r; r = r->next) { /* write boundary-header */ b = chunkqueue_get_append_buffer(con->send); buffer_copy_string_len(b, CONST_STR_LEN("\r\n--")); buffer_append_string(b, boundary); /* write Content-Range */ buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes ")); buffer_append_off_t(b, r->start); buffer_append_string_len(b, CONST_STR_LEN("-")); buffer_append_off_t(b, r->end); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_off_t(b, sce->st.st_size); buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: ")); buffer_append_string_buffer(b, content_type); /* write END-OF-HEADER */ buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); con->response.content_length += b->used - 1; con->send->bytes_in += b->used - 1; chunkqueue_append_file(con->send, con->physical.path, r->start, r->end - r->start + 1); con->response.content_length += r->end - r->start + 1; con->send->bytes_in += r->end - r->start + 1; } /* add boundary end */ b = chunkqueue_get_append_buffer(con->send); buffer_copy_string_len(b, "\r\n--", 4); buffer_append_string(b, boundary); buffer_append_string_len(b, "--\r\n", 4); con->response.content_length += b->used - 1; con->send->bytes_in += b->used - 1; /* set header-fields */ buffer_copy_string_len(p->range_buf, CONST_STR_LEN("multipart/byteranges; boundary=")); buffer_append_string(p->range_buf, boundary); /* overwrite content-type */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf)); } else { r = ranges; chunkqueue_append_file(con->send, con->physical.path, r->start, r->end - r->start + 1); con->response.content_length += r->end - r->start + 1; con->send->bytes_in += r->end - r->start + 1; buffer_copy_string_len(p->range_buf, CONST_STR_LEN("bytes ")); buffer_append_off_t(p->range_buf, r->start); buffer_append_string_len(p->range_buf, CONST_STR_LEN("-")); buffer_append_off_t(p->range_buf, r->end); buffer_append_string_len(p->range_buf, CONST_STR_LEN("/")); buffer_append_off_t(p->range_buf, sce->st.st_size); response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf)); } /* ok, the file is set-up */ return 0; }
static int ssi_expr_tokenizer(server *srv, connection *con, plugin_data *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(p->ssi_cgi_env, token->ptr))) { buffer_copy_buffer(token, ds->value); } else if (NULL != (ds = (data_string *)array_get_element(p->ssi_vars, token->ptr))) { 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; }
static int aicloud_connection_smb_info_init(server *srv, connection *con, plugin_data *p) { UNUSED(srv); char pWorkgroup[30]={0}; char pServer[64]={0}; char pShare[1280]={0}; char pPath[1280]={0}; smbc_wrapper_parse_path2(con, pWorkgroup, pServer, pShare, pPath); buffer* bworkgroup = buffer_init(); buffer* bserver = buffer_init(); buffer* bshare = buffer_init(); buffer* bpath = buffer_init(); URI_QUERY_TYPE qflag = SMB_FILE_QUERY; if(pWorkgroup[0] != '\0') buffer_copy_string(bworkgroup, pWorkgroup); if(pServer[0] != '\0') { int isHost = smbc_check_connectivity(con->physical_auth_url->ptr); if(isHost) { buffer_copy_string(bserver, pServer); } else{ buffer_free(bworkgroup); buffer_free(bserver); buffer_free(bshare); buffer_free(bpath); return 2; } } else { if(qflag == SMB_FILE_QUERY) { qflag = SMB_HOST_QUERY; } } if(pServer[0] != '\0' && pShare[0] != '\0') { buffer_copy_string(bshare, pShare); } else { if(qflag == SMB_FILE_QUERY) { qflag = SMB_SHARE_QUERY; } } if(pServer[0] != '\0' && pShare[0] != '\0' && pPath[0] != '\0') { buffer_copy_string(bpath, pPath); qflag = SMB_FILE_QUERY; } data_string *ds = (data_string *)array_get_element(con->request.headers, "user-Agent"); smb_info_t *smb_info; if( ds && ( strstr( ds->value->ptr, "Mozilla" ) || strstr( ds->value->ptr, "Opera" ) || con->mode == SMB_NTLM ) ){ //- From browser, like IE, Chrome, Firefox, Safari if(smb_info = smbdav_get_smb_info_from_pool(srv, con, p)){ Cdbg(DBE, "Get smb_info from pool smb_info->qflag=[%d], smb_info->user=[%s], smb_info->pass=[%s]", smb_info->qflag, smb_info->username->ptr, smb_info->password->ptr); } else{ smb_info = calloc(1, sizeof(smb_info_t)); smb_info->username = buffer_init(); smb_info->password = buffer_init(); smb_info->workgroup = buffer_init(); smb_info->server = buffer_init(); smb_info->share = buffer_init(); smb_info->path = buffer_init(); smb_info->user_agent = buffer_init(); smb_info->src_ip = buffer_init(); if(con->mode == SMB_NTLM){ smb_info->cli = smbc_cli_initialize(); if(!buffer_is_empty(bserver)){ smbc_cli_connect(smb_info->cli, bserver->ptr, SMB_PORT); } smb_info->ntlmssp_state = NULL; smb_info->state = NTLMSSP_INITIAL; } DLIST_ADD(p->smb_info_list, smb_info); } con->smb_info = smb_info; } else{ smb_info = calloc(1, sizeof(smb_info_t)); smb_info->username = buffer_init(); smb_info->password = buffer_init(); smb_info->workgroup = buffer_init(); smb_info->server = buffer_init(); smb_info->share = buffer_init(); smb_info->path = buffer_init(); smb_info->user_agent = buffer_init(); smb_info->src_ip = buffer_init(); con->smb_info = smb_info; } con->smb_info->auth_time = time(NULL); con->smb_info->auth_right = 0; if(ds) buffer_copy_string(con->smb_info->user_agent, ds->value->ptr); con->smb_info->qflag = qflag; buffer_copy_string_buffer(con->smb_info->workgroup, bworkgroup); buffer_copy_string_buffer(con->smb_info->server, bserver); buffer_copy_string_buffer(con->smb_info->share, bshare); buffer_copy_string_buffer(con->smb_info->path, bpath); buffer_copy_string_buffer(con->smb_info->src_ip, con->dst_addr_buf); Cdbg(DBE, "con->smb_info->workgroup=[%s]", con->smb_info->workgroup->ptr); Cdbg(DBE, "con->smb_info->server=[%s]", con->smb_info->server->ptr); Cdbg(DBE, "con->smb_info->share=[%s]", con->smb_info->share->ptr); Cdbg(DBE, "con->smb_info->path=[%s]", con->smb_info->path->ptr); Cdbg(DBE, "con->smb_info->user_agent=[%s]", con->smb_info->user_agent->ptr); Cdbg(DBE, "con->smb_info->src_ip=[%s]", con->smb_info->src_ip->ptr); Cdbg(DBE, "con->smb_info->qflag=[%d]", con->smb_info->qflag); buffer_free(bworkgroup); buffer_free(bserver); buffer_free(bshare); buffer_free(bpath); return 1; }
/** * handle all header and content read * * we get called by the state-engine and by the fdevent-handler */ static int connection_handle_read_state(server *srv, connection *con) { connection_state_t ostate = con->state; chunk *c, *last_chunk; off_t last_offset; chunkqueue *cq = con->read_queue; chunkqueue *dst_cq = con->request_content_queue; int is_closed = 0; /* the connection got closed, if we don't have a complete header, -> error */ if (con->is_readable) { con->read_idle_ts = srv->cur_ts; switch(connection_handle_read(srv, con)) { case -1: return -1; case -2: is_closed = 1; break; default: break; } } /* the last chunk might be empty */ for (c = cq->first; c;) { if (cq->first == c && c->mem->used == 0) { /* the first node is empty */ /* ... and it is empty, move it to unused */ cq->first = c->next; if (cq->first == NULL) cq->last = NULL; c->next = cq->unused; cq->unused = c; cq->unused_chunks++; c = cq->first; } else if (c->next && c->next->mem->used == 0) { chunk *fc; /* next node is the last one */ /* ... and it is empty, move it to unused */ fc = c->next; c->next = fc->next; fc->next = cq->unused; cq->unused = fc; cq->unused_chunks++; /* the last node was empty */ if (c->next == NULL) { cq->last = c; } c = c->next; } else { c = c->next; } } /* we might have got several packets at once */ switch(ostate) { case CON_STATE_READ: /* if there is a \r\n\r\n in the chunkqueue * * scan the chunk-queue twice * 1. to find the \r\n\r\n * 2. to copy the header-packet * */ last_chunk = NULL; last_offset = 0; for (c = cq->first; c; c = c->next) { buffer b; size_t i; b.ptr = c->mem->ptr + c->offset; b.used = c->mem->used - c->offset; if (b.used > 0) b.used--; /* buffer "used" includes terminating zero */ for (i = 0; i < b.used; i++) { char ch = b.ptr[i]; if ('\r' == ch) { /* chec if \n\r\n follows */ size_t j = i+1; chunk *cc = c; const char header_end[] = "\r\n\r\n"; int header_end_match_pos = 1; for ( ; cc; cc = cc->next, j = 0 ) { buffer bb; bb.ptr = cc->mem->ptr + cc->offset; bb.used = cc->mem->used - cc->offset; if (bb.used > 0) bb.used--; /* buffer "used" includes terminating zero */ for ( ; j < bb.used; j++) { ch = bb.ptr[j]; if (ch == header_end[header_end_match_pos]) { header_end_match_pos++; if (4 == header_end_match_pos) { last_chunk = cc; last_offset = j+1; goto found_header_end; } } else { goto reset_search; } } } } reset_search: ; } } found_header_end: /* found */ if (last_chunk) { buffer_reset(con->request.request); for (c = cq->first; c; c = c->next) { buffer b; b.ptr = c->mem->ptr + c->offset; b.used = c->mem->used - c->offset; if (c == last_chunk) { b.used = last_offset + 1; } buffer_append_string_buffer(con->request.request, &b); if (c == last_chunk) { c->offset += last_offset; break; } else { /* the whole packet was copied */ c->offset = c->mem->used - 1; } } connection_set_state(srv, con, CON_STATE_REQUEST_END); } else if (chunkqueue_length(cq) > 64 * 1024) { log_error_write(srv, __FILE__, __LINE__, "s", "oversized request-header -> sending Status 414"); con->http_status = 414; /* Request-URI too large */ con->keep_alive = 0; connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); } break; case CON_STATE_READ_POST: // xb ready to mod for (c = cq->first; c && (dst_cq->bytes_in != (off_t)con->request.content_length); c = c->next) { off_t weWant, weHave, toRead; weWant = con->request.content_length - dst_cq->bytes_in; assert(c->mem->used); weHave = c->mem->used - c->offset - 1; toRead = weHave > weWant ? weWant : weHave; data_string *ds_pi = (data_string *)array_get_element(con->request.headers, "X-Pi-Upload"); if (ds_pi) { float id; sscanf(ds_pi->value->ptr, "%f", &id); if (id != con->upload_id) { log_error_write(srv, __FILE__, __LINE__, "s", "newfile"); char path[256]; sprintf(path, "/tmp/upload_vid/%f", id); int fd = open(path, O_CREAT | O_WRONLY, 0777); con->upload_fd = fd; con->upload_id = id; } log_error_write(srv, __FILE__, __LINE__, "sboood", "pi-upload:", ds_pi->value, weHave, (off_t)dst_cq->bytes_in, (off_t)con->request.content_length, (int)ds_pi); write(con->upload_fd, c->mem->ptr + c->offset, toRead); /* dst_cq->bytes_in += toRead; if (dst_cq->bytes_in == (off_t)con->request.content_length) { connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; } continue; */ } /* the new way, copy everything into a chunkqueue whcih might use tempfiles */ if (con->request.content_length > 64 * 1024) { chunk *dst_c = NULL; /* copy everything to max 1Mb sized tempfiles */ /* * if the last chunk is * - smaller than 1Mb (size < 1Mb) * - not read yet (offset == 0) * -> append to it * otherwise * -> create a new chunk * * */ // xb mod: my upload file if (dst_cq->last && dst_cq->last->type == FILE_CHUNK && dst_cq->last->file.is_temp && dst_cq->last->offset == 0) { /* ok, take the last chunk for our job */ // xb mod: my upload file if (0) { /* dst_c = chunkqueue_get_append_tempfile(dst_cq); close(dst_c->file.fd); buffer *path = buffer_init_string("/var/cache/lighttpd/uploads/my-upload-"); buffer_append_string_buffer(path, ds->value); dst_c->file.fd = open(path->ptr, O_WRONLY | O_CREAT, 0777); log_error_write(srv, __FILE__, __LINE__, "sb", "pi-upload: path", path); #ifdef FD_CLOEXEC fcntl(dst_c->file.fd, F_SETFD, FD_CLOEXEC); #endif buffer_free(path); */ } else { if (dst_cq->last->file.length < 1 * 1024 * 1024) { dst_c = dst_cq->last; if (dst_c->file.fd == -1) { /* this should not happen as we cache the fd, but you never know */ dst_c->file.fd = open(dst_c->file.name->ptr, O_WRONLY | O_APPEND); #ifdef FD_CLOEXEC fcntl(dst_c->file.fd, F_SETFD, FD_CLOEXEC); #endif } } else { /* the chunk is too large now, close it */ dst_c = dst_cq->last; if (dst_c->file.fd != -1) { close(dst_c->file.fd); dst_c->file.fd = -1; } dst_c = chunkqueue_get_append_tempfile(dst_cq); } } } else { dst_c = chunkqueue_get_append_tempfile(dst_cq); } /* we have a chunk, let's write to it */ if (dst_c->file.fd == -1) { /* we don't have file to write to, * EACCES might be one reason. * * Instead of sending 500 we send 413 and say the request is too large * */ log_error_write(srv, __FILE__, __LINE__, "sbs", "denying upload as opening to temp-file for upload failed:", dst_c->file.name, strerror(errno)); con->http_status = 413; /* Request-Entity too large */ con->keep_alive = 0; connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; } // xb mod //if (toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) { if (!ds_pi && toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) { /* write failed for some reason ... disk full ? */ log_error_write(srv, __FILE__, __LINE__, "sbs", "denying upload as writing to file failed:", dst_c->file.name, strerror(errno)); con->http_status = 413; /* Request-Entity too large */ con->keep_alive = 0; connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); close(dst_c->file.fd); dst_c->file.fd = -1; break; } dst_c->file.length += toRead; if (dst_cq->bytes_in + toRead == (off_t)con->request.content_length) { /* we read everything, close the chunk */ close(dst_c->file.fd); dst_c->file.fd = -1; } } else { buffer *b; if (dst_cq->last && dst_cq->last->type == MEM_CHUNK) { b = dst_cq->last->mem; } else { b = chunkqueue_get_append_buffer(dst_cq); /* prepare buffer size for remaining POST data; is < 64kb */ buffer_prepare_copy(b, con->request.content_length - dst_cq->bytes_in + 1); } buffer_append_string_len(b, c->mem->ptr + c->offset, toRead); } c->offset += toRead; dst_cq->bytes_in += toRead; } /* Content is ready */ if (dst_cq->bytes_in == (off_t)con->request.content_length) { connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); log_error_write(srv, __FILE__, __LINE__, "s", "endfile: ok"); } break; default: break; } /* the connection got closed and we didn't got enough data to leave one of the READ states * the only way is to leave here */ if (is_closed && ostate == con->state) { connection_set_state(srv, con, CON_STATE_ERROR); log_error_write(srv, __FILE__, __LINE__, "s", "endfile: error"); } chunkqueue_remove_finished_chunks(cq); return 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; pos = strrchr(fn, #ifdef __WIN32 '\\' #else '/' #endif ); if (pos) { buffer_copy_string_len(context.basedir, fn, pos - fn + 1); fn = pos + 1; } dc = data_config_init(); buffer_copy_string(dc->key, "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(dpid->key, "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(dcwd->key, "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(ds->value, "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(ds->value, "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(ds->value, "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(ds->value, "mod_indexfile"); array_insert_unique(modules->value, (data_unset *)ds); ds = data_string_init(); buffer_copy_string(ds->value, "mod_dirlisting"); array_insert_unique(modules->value, (data_unset *)ds); ds = data_string_init(); buffer_copy_string(ds->value, "mod_staticfile"); array_insert_unique(modules->value, (data_unset *)ds); buffer_copy_string(modules->key, "server.modules"); array_insert_unique(srv->config, (data_unset *)modules); } if (0 != config_insert(srv)) { return -1; } 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 "REMOTE_USER", for example * ssl.verifyclient.username = "******" or "SSL_CLIENT_S_DN_emailAddress" */ if (buffer_is_equal(con->conf.ssl_verifyclient_username, envds->key)) { data_string *ds; if (NULL == (ds = (data_string *)array_get_element(con->environment, "REMOTE_USER"))) { if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string(ds->key, "REMOTE_USER"); array_insert_unique(con->environment, (data_unset *)ds); } buffer_copy_buffer(ds->value, 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_string_prepare_copy(envds->value, n); BIO_read(bio, envds->value->ptr, n); BIO_free(bio); buffer_commit(envds->value, n); array_insert_unique(con->environment, (data_unset *)envds); } } X509_free(xs); }
int main (int argc, char **argv) { server *srv = NULL; int print_config = 0; int test_config = 0; int i_am_root; int o; int num_childs = 0; int pid_fd = -1, fd; size_t i; #ifdef HAVE_SIGACTION struct sigaction act; #endif #ifdef HAVE_GETRLIMIT struct rlimit rlim; #endif #ifdef USE_ALARM struct itimerval interval; interval.it_interval.tv_sec = 1; interval.it_interval.tv_usec = 0; interval.it_value.tv_sec = 1; interval.it_value.tv_usec = 0; #endif /* for nice %b handling in strfime() */ setlocale(LC_TIME, "C"); if (NULL == (srv = server_init())) { fprintf(stderr, "did this really happen?\n"); return -1; } /* init structs done */ srv->srvconf.port = 0; #ifdef HAVE_GETUID i_am_root = (getuid() == 0); #else i_am_root = 0; #endif srv->srvconf.dont_daemonize = 0; while(-1 != (o = getopt(argc, argv, "f:m:hvVDpt"))) { switch(o) { case 'f': if (config_read(srv, optarg)) { server_free(srv); return -1; } break; case 'm': buffer_copy_string(srv->srvconf.modules_dir, optarg); break; case 'p': print_config = 1; break; case 't': test_config = 1; break; case 'D': srv->srvconf.dont_daemonize = 1; break; case 'v': show_version(); return 0; case 'V': show_features(); return 0; case 'h': show_help(); return 0; default: show_help(); server_free(srv); return -1; } } if (!srv->config_storage) { log_error_write(srv, __FILE__, __LINE__, "s", "No configuration available. Try using -f option."); server_free(srv); return -1; } if (print_config) { data_unset *dc = srv->config_context->data[0]; if (dc) { dc->print(dc, 0); fprintf(stdout, "\n"); } else { /* shouldn't happend */ fprintf(stderr, "global config not found\n"); } } if (test_config) { printf("Syntax OK\n"); } if (test_config || print_config) { server_free(srv); return 0; } /* close stdin and stdout, as they are not needed */ /* move stdin to /dev/null */ if (-1 != (fd = open("/dev/null", O_RDONLY))) { close(STDIN_FILENO); dup2(fd, STDIN_FILENO); close(fd); } /* move stdout to /dev/null */ if (-1 != (fd = open("/dev/null", O_WRONLY))) { close(STDOUT_FILENO); dup2(fd, STDOUT_FILENO); close(fd); } if (0 != config_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting default values failed"); server_free(srv); return -1; } /* UID handling */ #ifdef HAVE_GETUID if (!i_am_root && (geteuid() == 0 || getegid() == 0)) { /* we are setuid-root */ log_error_write(srv, __FILE__, __LINE__, "s", "Are you nuts ? Don't apply a SUID bit to this binary"); server_free(srv); return -1; } #endif /* check document-root */ if (srv->config_storage[0]->document_root->used <= 1) { log_error_write(srv, __FILE__, __LINE__, "s", "document-root is not set\n"); server_free(srv); return -1; } if (plugins_load(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "loading plugins finally failed"); plugins_free(srv); server_free(srv); return -1; } /* open pid file BEFORE chroot */ if (srv->srvconf.pid_file->used) { if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { struct stat st; if (errno != EEXIST) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } if (0 != stat(srv->srvconf.pid_file->ptr, &st)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "stating existing pid-file failed:", srv->srvconf.pid_file, strerror(errno)); } if (!S_ISREG(st.st_mode)) { log_error_write(srv, __FILE__, __LINE__, "sb", "pid-file exists and isn't regular file:", srv->srvconf.pid_file); return -1; } if (-1 == (pid_fd = open(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; } } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* select limits itself * * as it is a hard limit and will lead to a segfault we add some safety * */ srv->max_fds = FD_SETSIZE - 200; } else { srv->max_fds = 4096; } if (i_am_root) { struct group *grp = NULL; struct passwd *pwd = NULL; int use_rlimit = 1; #ifdef HAVE_VALGRIND_VALGRIND_H if (RUNNING_ON_VALGRIND) use_rlimit = 0; #endif #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } if (use_rlimit && srv->srvconf.max_fds) { /* set rlimits */ rlim.rlim_cur = srv->srvconf.max_fds; rlim.rlim_max = srv->srvconf.max_fds; if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } /* #372: solaris need some fds extra for devpoll */ if (rlim.rlim_cur > 10) rlim.rlim_cur -= 10; if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (use_rlimit && srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > FD_SETSIZE - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } #ifdef HAVE_PWD_H /* set user and group */ if (srv->srvconf.username->used) { if (NULL == (pwd = getpwnam(srv->srvconf.username->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find username", srv->srvconf.username); return -1; } if (pwd->pw_uid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set uid to 0\n"); return -1; } } if (srv->srvconf.groupname->used) { if (NULL == (grp = getgrnam(srv->srvconf.groupname->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "can't find groupname", srv->srvconf.groupname); return -1; } if (grp->gr_gid == 0) { log_error_write(srv, __FILE__, __LINE__, "s", "I will not set gid to 0\n"); return -1; } } #endif /* we need root-perms for port < 1024 */ if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } #ifdef HAVE_CHROOT if (srv->srvconf.changeroot->used) { tzset(); if (-1 == chroot(srv->srvconf.changeroot->ptr)) { log_error_write(srv, __FILE__, __LINE__, "ss", "chroot failed: ", strerror(errno)); return -1; } if (-1 == chdir("/")) { log_error_write(srv, __FILE__, __LINE__, "ss", "chdir failed: ", strerror(errno)); return -1; } } #endif #ifdef HAVE_PWD_H /* drop root privs */ if (srv->srvconf.groupname->used) { setgid(grp->gr_gid); setgroups(0, NULL); } if (srv->srvconf.username->used) { if (srv->srvconf.groupname->used) { initgroups(srv->srvconf.username->ptr, grp->gr_gid); } setuid(pwd->pw_uid); } #endif #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) /** * on IRIX 6.5.30 they have prctl() but no DUMPABLE */ if (srv->srvconf.enable_cores) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif } else { #ifdef HAVE_GETRLIMIT if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't get 'max filedescriptors'", strerror(errno)); return -1; } /** * we are not root can can't increase the fd-limit, but we can reduce it */ if (srv->srvconf.max_fds && srv->srvconf.max_fds < rlim.rlim_cur) { /* set rlimits */ rlim.rlim_cur = srv->srvconf.max_fds; if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { log_error_write(srv, __FILE__, __LINE__, "ss", "couldn't set 'max filedescriptors'", strerror(errno)); return -1; } } if (srv->event_handler == FDEVENT_HANDLER_SELECT) { srv->max_fds = rlim.rlim_cur < FD_SETSIZE - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; } else { srv->max_fds = rlim.rlim_cur; } /* set core file rlimit, if enable_cores is set */ if (srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_CORE, &rlim); } #endif if (srv->event_handler == FDEVENT_HANDLER_SELECT) { /* don't raise the limit above FD_SET_SIZE */ if (srv->max_fds > FD_SETSIZE - 200) { log_error_write(srv, __FILE__, __LINE__, "sd", "can't raise max filedescriptors above", FD_SETSIZE - 200, "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); return -1; } } if (0 != network_init(srv)) { plugins_free(srv); server_free(srv); return -1; } } /* set max-conns */ if (srv->srvconf.max_conns > srv->max_fds) { /* we can't have more connections than max-fds */ srv->max_conns = srv->max_fds; } else if (srv->srvconf.max_conns) { /* otherwise respect the wishes of the user */ srv->max_conns = srv->srvconf.max_conns; } else { /* or use the default */ srv->max_conns = srv->max_fds; } if (HANDLER_GO_ON != plugins_call_init(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Initialization of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_FORK /* network is up, let's deamonize ourself */ if (srv->srvconf.dont_daemonize == 0) daemonize(); #endif srv->gid = getgid(); srv->uid = getuid(); /* write pid file */ if (pid_fd != -1) { buffer_copy_long(srv->tmp_buf, getpid()); buffer_append_string(srv->tmp_buf, "\n"); write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1); close(pid_fd); pid_fd = -1; } if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* dump unused config-keys */ for (i = 0; i < srv->config_context->used; i++) { array *config = ((data_config *)srv->config_context->data[i])->value; size_t j; for (j = 0; config && j < config->used; j++) { data_unset *du = config->data[j]; /* all var.* is known as user defined variable */ if (strncmp(du->key->ptr, "var.", sizeof("var.") - 1) == 0) { continue; } if (NULL == array_get_element(srv->config_touched, du->key->ptr)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "WARNING: unknown config-key:", du->key, "(ignored)"); } } } if (srv->config_unsupported) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains unsupported keys. Going down."); } if (srv->config_deprecated) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains deprecated keys. Going down."); } if (srv->config_unsupported || srv->config_deprecated) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } if (-1 == log_error_open(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "opening errorlog failed, dying"); plugins_free(srv); network_close(srv); server_free(srv); return -1; } #ifdef HAVE_SIGACTION memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; sigaction(SIGPIPE, &act, NULL); sigaction(SIGUSR1, &act, NULL); # if defined(SA_SIGINFO) act.sa_sigaction = sigaction_handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; # else act.sa_handler = signal_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; # endif sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGALRM, &act, NULL); sigaction(SIGCHLD, &act, NULL); #elif defined(HAVE_SIGNAL) /* ignore the SIGPIPE from sendfile() */ signal(SIGPIPE, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGALRM, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); signal(SIGCHLD, signal_handler); signal(SIGINT, signal_handler); #endif #ifdef USE_ALARM signal(SIGALRM, signal_handler); /* setup periodic timer (1 second) */ if (setitimer(ITIMER_REAL, &interval, NULL)) { log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed"); return -1; } getitimer(ITIMER_REAL, &interval); #endif #ifdef HAVE_FORK /* start watcher and workers */ num_childs = srv->srvconf.max_worker; if (num_childs > 0) { int child = 0; while (!child && !srv_shutdown && !graceful_shutdown) { if (num_childs > 0) { switch (fork()) { case -1: return -1; case 0: child = 1; break; default: num_childs--; break; } } else { int status; if (-1 != wait(&status)) { /** * one of our workers went away */ num_childs++; } else { switch (errno) { case EINTR: /** * if we receive a SIGHUP we have to close our logs ourself as we don't * have the mainloop who can help us here */ if (handle_sig_hup) { handle_sig_hup = 0; log_error_cycle(srv); /** * forward to all procs in the process-group * * we also send it ourself */ if (!forwarded_sig_hup) { forwarded_sig_hup = 1; kill(0, SIGHUP); } } break; default: break; } } } } /** * for the parent this is the exit-point */ if (!child) { /** * kill all children too */ if (graceful_shutdown) { kill(0, SIGINT); } else if (srv_shutdown) { kill(0, SIGTERM); } log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; } } #endif if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) { log_error_write(srv, __FILE__, __LINE__, "s", "fdevent_init failed"); return -1; } /* * kqueue() is called here, select resets its internals, * all server sockets get their handlers * * */ if (0 != network_register_fdevents(srv)) { plugins_free(srv); network_close(srv); server_free(srv); return -1; } /* might fail if user is using fam (not gamin) and famd isn't running */ if (NULL == (srv->stat_cache = stat_cache_init())) { log_error_write(srv, __FILE__, __LINE__, "s", "stat-cache could not be setup, dieing."); return -1; } #ifdef HAVE_FAM_H /* setup FAM */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != FAMOpen2(srv->stat_cache->fam, "lighttpd")) { log_error_write(srv, __FILE__, __LINE__, "s", "could not open a fam connection, dieing."); return -1; } #ifdef HAVE_FAMNOEXISTS FAMNoExists(srv->stat_cache->fam); #endif srv->stat_cache->fam_fcce_ndx = -1; fdevent_register(srv->ev, FAMCONNECTION_GETFD(srv->stat_cache->fam), stat_cache_handle_fdevent, NULL); fdevent_event_add(srv->ev, &(srv->stat_cache->fam_fcce_ndx), FAMCONNECTION_GETFD(srv->stat_cache->fam), FDEVENT_IN); } #endif /* get the current number of FDs */ srv->cur_fds = open("/dev/null", O_RDONLY); close(srv->cur_fds); for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); return -1; } } /* main-loop */ while (!srv_shutdown) { int n; size_t ndx; time_t min_ts; if (handle_sig_hup) { handler_t r; /* reset notification */ handle_sig_hup = 0; /* cycle logfiles */ switch(r = plugins_call_handle_sighup(srv)) { case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "sd", "sighup-handler return with an error", r); break; } if (-1 == log_error_cycle(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "cycling errorlog failed, dying"); return -1; } else { #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "logfiles cycled UID =", last_sighup_info.si_uid, "PID =", last_sighup_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "logfiles cycled"); #endif } } if (handle_sig_alarm) { /* a new second */ #ifdef USE_ALARM /* reset notification */ handle_sig_alarm = 0; #endif /* get current time */ min_ts = time(NULL); if (min_ts != srv->cur_ts) { int cs = 0; connections *conns = srv->conns; handler_t r; switch(r = plugins_call_handle_trigger(srv)) { case HANDLER_GO_ON: break; case HANDLER_ERROR: log_error_write(srv, __FILE__, __LINE__, "s", "one of the triggers failed"); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } /* trigger waitpid */ srv->cur_ts = min_ts; /* cleanup stat-cache */ stat_cache_trigger_cleanup(srv); /** * check all connections for timeouts * */ for (ndx = 0; ndx < conns->used; ndx++) { int changed = 0; connection *con; int t_diff; con = conns->ptr[ndx]; if (con->state == CON_STATE_READ || con->state == CON_STATE_READ_POST) { if (con->request_count == 1) { if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { /* time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } else { if (srv->cur_ts - con->read_idle_ts > con->conf.max_keep_alive_idle) { /* time - out */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - read-timeout:", con->fd); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } } if ((con->state == CON_STATE_WRITE) && (con->write_request_ts != 0)) { #if 0 if (srv->cur_ts - con->write_request_ts > 60) { log_error_write(srv, __FILE__, __LINE__, "sdd", "connection closed - pre-write-request-timeout:", con->fd, srv->cur_ts - con->write_request_ts); } #endif if (srv->cur_ts - con->write_request_ts > con->conf.max_write_idle) { /* time - out */ #if 1 log_error_write(srv, __FILE__, __LINE__, "sbsosds", "NOTE: a request for", con->request.uri, "timed out after writing", con->bytes_written, "bytes. We waited", (int)con->conf.max_write_idle, "seconds. If this a problem increase server.max-write-idle"); #endif connection_set_state(srv, con, CON_STATE_ERROR); changed = 1; } } /* we don't like div by zero */ if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1; if (con->traffic_limit_reached && (con->conf.kbytes_per_second == 0 || ((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))) { /* enable connection again */ con->traffic_limit_reached = 0; changed = 1; } if (changed) { connection_state_machine(srv, con); } con->bytes_written_cur_second = 0; *(con->conf.global_bytes_per_second_cnt_ptr) = 0; #if 0 if (cs == 0) { fprintf(stderr, "connection-state: "); cs = 1; } fprintf(stderr, "c[%d,%d]: %s ", con->fd, con->fcgi.fd, connection_get_state(con->state)); #endif } if (cs == 1) fprintf(stderr, "\n"); } } if (srv->sockets_disabled) { /* our server sockets are disabled, why ? */ if ((srv->cur_fds + srv->want_fds < srv->max_fds * 0.8) && /* we have enough unused fds */ (srv->conns->used < srv->max_conns * 0.9) && (0 == graceful_shutdown)) { for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_add(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); } log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets enabled again"); srv->sockets_disabled = 0; } } else { if ((srv->cur_fds + srv->want_fds > srv->max_fds * 0.9) || /* out of fds */ (srv->conns->used > srv->max_conns) || /* out of connections */ (graceful_shutdown)) { /* graceful_shutdown */ /* disable server-fds */ for (i = 0; i < srv->srv_sockets.used; i++) { server_socket *srv_socket = srv->srv_sockets.ptr[i]; fdevent_event_del(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd); if (graceful_shutdown) { /* we don't want this socket anymore, * * closing it right away will make it possible for * the next lighttpd to take over (graceful restart) * */ fdevent_unregister(srv->ev, srv_socket->fd); close(srv_socket->fd); srv_socket->fd = -1; /* network_close() will cleanup after us */ if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } } } if (graceful_shutdown) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] graceful shutdown started"); } else if (srv->conns->used > srv->max_conns) { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, connection limit reached"); } else { log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, out-of-fds"); } srv->sockets_disabled = 1; } } if (graceful_shutdown && srv->conns->used == 0) { /* we are in graceful shutdown phase and all connections are closed * we are ready to terminate without harming anyone */ srv_shutdown = 1; } /* we still have some fds to share */ if (srv->want_fds) { /* check the fdwaitqueue for waiting fds */ int free_fds = srv->max_fds - srv->cur_fds - 16; connection *con; for (; free_fds > 0 && NULL != (con = fdwaitqueue_unshift(srv, srv->fdwaitqueue)); free_fds--) { connection_state_machine(srv, con); srv->want_fds--; } } if ((n = fdevent_poll(srv->ev, 1000)) > 0) { /* n is the number of events */ int revents; int fd_ndx; #if 0 if (n > 0) { log_error_write(srv, __FILE__, __LINE__, "sd", "polls:", n); } #endif fd_ndx = -1; do { fdevent_handler handler; void *context; handler_t r; fd_ndx = fdevent_event_next_fdndx (srv->ev, fd_ndx); revents = fdevent_event_get_revent (srv->ev, fd_ndx); fd = fdevent_event_get_fd (srv->ev, fd_ndx); handler = fdevent_get_handler(srv->ev, fd); context = fdevent_get_context(srv->ev, fd); /* connection_handle_fdevent needs a joblist_append */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sdd", "event for", fd, revents); #endif switch (r = (*handler)(srv, context, revents)) { case HANDLER_FINISHED: case HANDLER_GO_ON: case HANDLER_WAIT_FOR_EVENT: case HANDLER_WAIT_FOR_FD: break; case HANDLER_ERROR: /* should never happen */ SEGFAULT(); break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } } while (--n > 0); } else if (n < 0 && errno != EINTR) { log_error_write(srv, __FILE__, __LINE__, "ss", "fdevent_poll failed:", strerror(errno)); } for (ndx = 0; ndx < srv->joblist->used; ndx++) { connection *con = srv->joblist->ptr[ndx]; handler_t r; connection_state_machine(srv, con); switch(r = plugins_call_handle_joblist(srv, con)) { case HANDLER_FINISHED: case HANDLER_GO_ON: break; default: log_error_write(srv, __FILE__, __LINE__, "d", r); break; } con->in_joblist = 0; } srv->joblist->used = 0; } if (srv->srvconf.pid_file->used && srv->srvconf.changeroot->used == 0 && 0 == graceful_shutdown) { if (0 != unlink(srv->srvconf.pid_file->ptr)) { if (errno != EACCES && errno != EPERM) { log_error_write(srv, __FILE__, __LINE__, "sbds", "unlink failed for:", srv->srvconf.pid_file, errno, strerror(errno)); } } } #ifdef HAVE_SIGACTION log_error_write(srv, __FILE__, __LINE__, "sdsd", "server stopped by UID =", last_sigterm_info.si_uid, "PID =", last_sigterm_info.si_pid); #else log_error_write(srv, __FILE__, __LINE__, "s", "server stopped"); #endif /* clean-up */ log_error_close(srv); network_close(srv); connections_free(srv); plugins_free(srv); server_free(srv); return 0; }
static int http_response_parse_range(server *srv, connection *con, plugin_data *p) { int multipart = 0; int error; off_t start, end; const char *s, *minus; char *boundary = "fkj49sn38dcn3"; data_string *ds; stat_cache_entry *sce = NULL; buffer *content_type = NULL; if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { SEGFAULT(); } start = 0; end = sce->st.st_size - 1; con->response.content_length = 0; if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) { content_type = ds->value; } for (s = con->request.http_range, error = 0; !error && *s && NULL != (minus = strchr(s, '-')); ) { char *err; off_t la, le; if (s == minus) { /* -<stop> */ le = strtoll(s, &err, 10); if (le == 0) { /* RFC 2616 - 14.35.1 */ con->http_status = 416; error = 1; } else if (*err == '\0') { /* end */ s = err; end = sce->st.st_size - 1; start = sce->st.st_size + le; } else if (*err == ',') { multipart = 1; s = err + 1; end = sce->st.st_size - 1; start = sce->st.st_size + le; } else { error = 1; } } else if (*(minus+1) == '\0' || *(minus+1) == ',') { /* <start>- */ la = strtoll(s, &err, 10); if (err == minus) { /* ok */ if (*(err + 1) == '\0') { s = err + 1; end = sce->st.st_size - 1; start = la; } else if (*(err + 1) == ',') { multipart = 1; s = err + 2; end = sce->st.st_size - 1; start = la; } else { error = 1; } } else { /* error */ error = 1; } } else { /* <start>-<stop> */ la = strtoll(s, &err, 10); if (err == minus) { le = strtoll(minus+1, &err, 10); /* RFC 2616 - 14.35.1 */ if (la > le) { error = 1; } if (*err == '\0') { /* ok, end*/ s = err; end = le; start = la; } else if (*err == ',') { multipart = 1; s = err + 1; end = le; start = la; } else { /* error */ error = 1; } } else { /* error */ error = 1; } } if (!error) { if (start < 0) start = 0; /* RFC 2616 - 14.35.1 */ if (end > sce->st.st_size - 1) end = sce->st.st_size - 1; if (start > sce->st.st_size - 1) { error = 1; con->http_status = 416; } } if (!error) { if (multipart) { /* write boundary-header */ buffer *b; b = chunkqueue_get_append_buffer(con->write_queue); buffer_copy_string_len(b, CONST_STR_LEN("\r\n--")); buffer_append_string(b, boundary); /* write Content-Range */ buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes ")); buffer_append_off_t(b, start); buffer_append_string_len(b, CONST_STR_LEN("-")); buffer_append_off_t(b, end); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_off_t(b, sce->st.st_size); buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: ")); buffer_append_string_buffer(b, content_type); /* write END-OF-HEADER */ buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); con->response.content_length += b->used - 1; } chunkqueue_append_file(con->write_queue, con->physical.path, start, end - start + 1); con->response.content_length += end - start + 1; } } /* something went wrong */ if (error) return -1; if (multipart) { /* add boundary end */ buffer *b; b = chunkqueue_get_append_buffer(con->write_queue); buffer_copy_string_len(b, "\r\n--", 4); buffer_append_string(b, boundary); buffer_append_string_len(b, "--\r\n", 4); con->response.content_length += b->used - 1; /* set header-fields */ buffer_copy_string_len(p->range_buf, CONST_STR_LEN("multipart/byteranges; boundary=")); buffer_append_string(p->range_buf, boundary); /* overwrite content-type */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf)); } else { /* add Content-Range-header */ buffer_copy_string_len(p->range_buf, CONST_STR_LEN("bytes ")); buffer_append_off_t(p->range_buf, start); buffer_append_string_len(p->range_buf, CONST_STR_LEN("-")); buffer_append_off_t(p->range_buf, end); buffer_append_string_len(p->range_buf, CONST_STR_LEN("/")); buffer_append_off_t(p->range_buf, sce->st.st_size); response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf)); } /* ok, the file is set-up */ return 0; }
/* parse config array */ int config_insert_values_internal(server *srv, array *ca, const config_values_t cv[]) { size_t i; data_unset *du; for (i = 0; cv[i].key; i++) { if (NULL == (du = array_get_element(ca, cv[i].key))) { /* no found */ continue; } switch (cv[i].type) { case T_CONFIG_ARRAY: if (du->type == TYPE_ARRAY) { size_t j; data_array *da = (data_array *)du; for (j = 0; j < da->value->used; j++) { if (da->value->data[j]->type == TYPE_STRING) { data_string *ds = data_string_init(); buffer_copy_string_buffer(ds->value, ((data_string *)(da->value->data[j]))->value); if (!da->is_index_key) { /* the id's were generated automaticly, as we copy now we might have to renumber them * this is used to prepend server.modules by mod_indexfile as it has to be loaded * before mod_fastcgi and friends */ buffer_copy_string_buffer(ds->key, ((data_string *)(da->value->data[j]))->key); } array_insert_unique(cv[i].destination, (data_unset *)ds); } else { log_error_write(srv, __FILE__, __LINE__, "sssd", "the key of an array can only be a string or a integer, variable:", cv[i].key, "type:", da->value->data[j]->type); return -1; } } } else { log_error_write(srv, __FILE__, __LINE__, "ss", cv[i].key, "should have been a array of strings like ... = ( \"...\" )"); return -1; } break; case T_CONFIG_STRING: if (du->type == TYPE_STRING) { data_string *ds = (data_string *)du; buffer_copy_string_buffer(cv[i].destination, ds->value); } else { log_error_write(srv, __FILE__, __LINE__, "ssss", cv[i].key, "should have been a string like ... = \"...\""); return -1; } break; case T_CONFIG_SHORT: switch(du->type) { case TYPE_INTEGER: { data_integer *di = (data_integer *)du; *((unsigned short *)(cv[i].destination)) = di->value; break; } case TYPE_STRING: { data_string *ds = (data_string *)du; /* If the value came from an environment variable, then it is a * data_string, although it may contain a number in ASCII * decimal format. We try to interpret the string as a decimal * short before giving up, in order to support setting numeric * values with environment variables (eg, port number). */ if (ds->value->ptr && *ds->value->ptr) { char *e; long l = strtol(ds->value->ptr, &e, 10); if (e != ds->value->ptr && !*e && l >=0 && l <= 65535) { *((unsigned short *)(cv[i].destination)) = l; break; } } log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected a short:", cv[i].key, ds->value); return -1; } default: log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected a short integer, range 0 ... 65535"); return -1; } break; case T_CONFIG_INT: switch(du->type) { case TYPE_INTEGER: { data_integer *di = (data_integer *)du; *((unsigned int *)(cv[i].destination)) = di->value; break; } case TYPE_STRING: { data_string *ds = (data_string *)du; if (ds->value->ptr && *ds->value->ptr) { char *e; long l = strtol(ds->value->ptr, &e, 10); if (e != ds->value->ptr && !*e && l >= 0) { *((unsigned int *)(cv[i].destination)) = l; break; } } log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected an integer:", cv[i].key, ds->value); return -1; } default: log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected an integer, range 0 ... 4294967295"); return -1; } break; case T_CONFIG_BOOLEAN: if (du->type == TYPE_STRING) { data_string *ds = (data_string *)du; if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) { *((unsigned short *)(cv[i].destination)) = 1; } else if (buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) { *((unsigned short *)(cv[i].destination)) = 0; } else { log_error_write(srv, __FILE__, __LINE__, "ssbs", "ERROR: unexpected value for key:", cv[i].key, ds->value, "(enable|disable)"); return -1; } } else { log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: unexpected type for key:", cv[i].key, "(string)", "\"(enable|disable)\""); return -1; } break; case T_CONFIG_LOCAL: case T_CONFIG_UNSET: break; case T_CONFIG_UNSUPPORTED: log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found unsupported key:", cv[i].key, "-", (char *)(cv[i].destination)); srv->config_unsupported = 1; break; case T_CONFIG_DEPRECATED: log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found deprecated key:", cv[i].key, "-", (char *)(cv[i].destination)); srv->config_deprecated = 1; break; } } return 0; }
static handler_t mod_evhost_uri_handler(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; size_t i; array *parsed_host; register char *ptr; int not_good = 0; stat_cache_entry *sce = NULL; /* not authority set */ if (con->uri.authority->used == 0) return HANDLER_GO_ON; mod_evhost_patch_connection(srv, con, p); /* missing even default(global) conf */ if (0 == p->conf.len) { return HANDLER_GO_ON; } parsed_host = array_init(); mod_evhost_parse_host(con, parsed_host); /* build document-root */ buffer_reset(p->tmp_buf); for (i = 0; i < p->conf.len; i++) { ptr = p->conf.path_pieces[i]->ptr; if (*ptr == '%') { data_string *ds; if (*(ptr+1) == '%') { /* %% */ buffer_append_string_len(p->tmp_buf,CONST_STR_LEN("%")); } else if (*(ptr+1) == '_' ) { /* %_ == full hostname */ char *colon = strchr(con->uri.authority->ptr, ':'); if(colon == NULL) { buffer_append_string_buffer(p->tmp_buf, con->uri.authority); /* adds fqdn */ } else { /* strip the port out of the authority-part of the URI scheme */ buffer_append_string_len(p->tmp_buf, con->uri.authority->ptr, colon - con->uri.authority->ptr); /* adds fqdn */ } } else if (NULL != (ds = (data_string *)array_get_element(parsed_host,p->conf.path_pieces[i]->ptr))) { if (ds->value->used) { buffer_append_string_buffer(p->tmp_buf,ds->value); } } else { /* unhandled %-sequence */ } } else { buffer_append_string_buffer(p->tmp_buf,p->conf.path_pieces[i]); } } BUFFER_APPEND_SLASH(p->tmp_buf); array_free(parsed_host); if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) { log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), p->tmp_buf); not_good = 1; } else if(!S_ISDIR(sce->st.st_mode)) { log_error_write(srv, __FILE__, __LINE__, "sb", "not a directory:", p->tmp_buf); not_good = 1; } if (!not_good) { buffer_copy_string_buffer(con->physical.doc_root, p->tmp_buf); } return HANDLER_GO_ON; }
int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { lua_State *L; int ret = -1; buffer *b; b = buffer_init(); /* push the lua file to the interpreter and see what happends */ L = luaL_newstate(); luaL_openlibs(L); /* register functions */ lua_register(L, "md5", f_crypto_md5); lua_register(L, "file_mtime", f_file_mtime); lua_register(L, "file_isreg", f_file_isreg); lua_register(L, "file_isdir", f_file_isreg); lua_register(L, "dir_files", f_dir_files); #ifdef USE_MEMCACHED lua_pushlightuserdata(L, p->conf.memc); lua_pushcclosure(L, f_memcache_get_long, 1); lua_setglobal(L, "memcache_get_long"); lua_pushlightuserdata(L, p->conf.memc); lua_pushcclosure(L, f_memcache_get_string, 1); lua_setglobal(L, "memcache_get_string"); lua_pushlightuserdata(L, p->conf.memc); lua_pushcclosure(L, f_memcache_exists, 1); lua_setglobal(L, "memcache_exists"); #endif /* register CGI environment */ lua_newtable(L); { int header_tbl = lua_gettop(L); c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)); if (!buffer_string_is_empty(con->request.pathinfo)) { c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); } c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir)); c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl)); } lua_setglobal(L, "request"); /* register GET parameter */ lua_newtable(L); { int get_tbl = lua_gettop(L); buffer_copy_buffer(b, con->uri.query); cache_export_get_params(L, get_tbl, b); buffer_reset(b); } lua_setglobal(L, "get"); /* 2 default constants */ lua_pushinteger(L, 0); lua_setglobal(L, "CACHE_HIT"); lua_pushinteger(L, 1); lua_setglobal(L, "CACHE_MISS"); /* load lua program */ ret = luaL_loadfile(L, fn->ptr); if (0 != ret) { log_error_write(srv, __FILE__, __LINE__, "sbsS", "failed loading cml_lua script", fn, ":", lua_tostring(L, -1)); goto error; } if (lua_pcall(L, 0, 1, 0)) { log_error_write(srv, __FILE__, __LINE__, "sbsS", "failed running cml_lua script", fn, ":", lua_tostring(L, -1)); goto error; } /* get return value */ ret = (int)lua_tointeger(L, -1); lua_pop(L, 1); /* fetch the data from lua */ lua_to_c_get_string(L, "trigger_handler", p->trigger_handler); if (0 == lua_to_c_get_string(L, "output_contenttype", b)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b)); } if (ret == 0) { /* up to now it is a cache-hit, check if all files exist */ int curelem; time_t mtime = 0; if (!lua_to_c_is_table(L, "output_include")) { log_error_write(srv, __FILE__, __LINE__, "s", "output_include is missing or not a table"); ret = -1; goto error; } lua_getglobal(L, "output_include"); curelem = lua_gettop(L); /* HOW-TO build a etag ? * as we don't just have one file we have to take the stat() * from all base files, merge them and build the etag from * it later. * * The mtime of the content is the mtime of the freshest base file * * */ lua_pushnil(L); /* first key */ while (lua_next(L, curelem) != 0) { /* key' is at index -2 and value' at index -1 */ if (lua_isstring(L, -1)) { const char *s = lua_tostring(L, -1); struct stat st; int fd; /* the file is relative, make it absolute */ if (s[0] != '/') { buffer_copy_buffer(b, p->basedir); buffer_append_string(b, lua_tostring(L, -1)); } else { buffer_copy_string(b, lua_tostring(L, -1)); } fd = stat_cache_open_rdonly_fstat(srv, con, b, &st); if (fd < 0) { /* stat failed */ switch(errno) { case ENOENT: /* a file is missing, call the handler to generate it */ if (!buffer_string_is_empty(p->trigger_handler)) { ret = 1; /* cache-miss */ log_error_write(srv, __FILE__, __LINE__, "s", "a file is missing, calling handler"); break; } else { /* handler not set -> 500 */ ret = -1; log_error_write(srv, __FILE__, __LINE__, "s", "a file missing and no handler set"); break; } break; default: break; } } else { chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size); if (st.st_mtime > mtime) mtime = st.st_mtime; } } else { /* not a string */ ret = -1; log_error_write(srv, __FILE__, __LINE__, "s", "not a string"); break; } lua_pop(L, 1); /* removes value'; keeps key' for next iteration */ } lua_settop(L, curelem - 1); if (ret == 0) { data_string *ds; char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; con->file_finished = 1; ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"); if (0 == mtime) mtime = time(NULL); /* default last-modified to now */ /* no Last-Modified specified */ if (NULL == ds) { strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime)); response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1); ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"); force_assert(NULL != ds); } if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, ds->value)) { /* ok, the client already has our content, * no need to send it again */ chunkqueue_reset(con->write_queue); ret = 0; /* cache-hit */ } } else { chunkqueue_reset(con->write_queue); } } if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) { /* cache-miss */ buffer_copy_buffer(con->uri.path, p->baseurl); buffer_append_string_buffer(con->uri.path, p->trigger_handler); buffer_copy_buffer(con->physical.path, p->basedir); buffer_append_string_buffer(con->physical.path, p->trigger_handler); chunkqueue_reset(con->write_queue); } error: lua_close(L); buffer_free(b); return ret /* cache-error */; }
handler_t basic_authentication_handler(server *srv, connection *con, plugin_data *p) { data_string *ds_auth = (data_string *)array_get_element(con->request.headers, "Authorization"); data_string *ds_useragent = (data_string *)array_get_element(con->request.headers, "user-Agent"); buffer *user = buffer_init(); buffer *pass = buffer_init(); buffer_copy_string(user, " "); buffer_copy_string(pass, " "); int get_account_from_smb_info = 0; char *auth_username = NULL; char *auth_password = NULL; if( smbc_parser_basic_authentication(srv, con, &auth_username, &auth_password) != 1 ){ if(con->smb_info==NULL) goto error_401; if( con->smb_info->username->used && con->smb_info->password->used ){ buffer_copy_string_buffer(user, con->smb_info->username); buffer_copy_string_buffer(pass, con->smb_info->password); get_account_from_smb_info = 1; } Cdbg(DBE, "fail smbc_parser_basic_authentication-> %s, %s", user->ptr, pass->ptr); } else{ buffer_copy_string(user, auth_username); buffer_copy_string(pass, auth_password); free(auth_username); free(auth_password); } time_t cur_time = time(NULL); double result = difftime(cur_time, con->smb_info->auth_time); if(con->smb_info->qflag == SMB_HOST_QUERY) { data_string *ds2 = (data_string *)array_get_element(con->request.headers, "user-Agent"); //- MUST login again more than 30 minutes if(result>1800){ goto error_401; } if( con->smb_info && buffer_is_equal_string(con->smb_info->username, "RELOGIN", 7) ){ buffer_reset(con->smb_info->username); buffer_reset(con->smb_info->password); goto error_401; } /* if(con->smb_info->username->used && con->smb_info->password->used){ buffer_copy_string_buffer(user, con->smb_info->username); buffer_copy_string_buffer(pass, con->smb_info->password); Cdbg(DBE, "SMB_HOST_QUERY-->copy from smb_info user=[%s], pass=[%s]", user->ptr, pass->ptr); } */ #if EMBEDDED_EANBLE char* webav_user = nvram_get_http_username(); char* webav_pass = nvram_get_http_passwd(); char* enable_webdav_block = nvram_get_enable_webdav_lock(); char* is_webdav_block = nvram_get_webdav_acc_lock(); int try_times = atoi(nvram_get_webdav_lock_times()); int try_interval = atoi(nvram_get_webdav_lock_interval())*60; #else char* webav_user = "******"; char* webav_pass = "******"; char* enable_webdav_block = "1"; char* is_webdav_block = g_is_webdav_block; int try_times = 3; int try_interval = 1*60; //- 1 minutes #endif int isBrowser = ( ds_useragent && (strstr( ds_useragent->value->ptr, "Mozilla" ) || strstr( ds_useragent->value->ptr, "Opera" ))) ? 1 : 0; if( isBrowser==1 && strcmp(enable_webdav_block, "1") == 0 && strcmp(is_webdav_block, "1") == 0 ){ Cdbg(DBE, "Direct go to 455 error page"); goto error_455; } if( strcmp(user->ptr, webav_user)!=0 || strcmp(pass->ptr, webav_pass)!=0 ){ if( isBrowser==1 && strcmp(enable_webdav_block, "1") == 0 && con->smb_info && ds_auth!=NULL ){ //if( isBrowser==1 && strcmp(enable_webdav_block, "1") == 0 && con->smb_info ){ con->smb_info->login_count++; time_t current_time = time(NULL); double result2 = difftime(cur_time, con->smb_info->login_begin_time); if( result2 > try_interval ){ con->smb_info->login_count = 1; } if(con->smb_info->login_count==1) con->smb_info->login_begin_time = time(NULL); Cdbg(DBE, "con->smb_info->login_count=[%d][%d]", con->smb_info->login_count, try_times); if(con->smb_info->login_count>=try_times){ con->smb_info->login_count = 0; #if EMBEDDED_EANBLE nvram_set_webdav_acc_lock("1"); #else g_is_webdav_block = "1"; #endif Cdbg(DBE, "error_455..."); goto error_455; } } goto error_401; } if(con->smb_info){ con->smb_info->login_count = 0; } if(!get_account_from_smb_info){ log_sys_write(srv, "ssss", "User", user->ptr, "login from ip", con->dst_addr_buf->ptr); buffer_copy_string_buffer(srv->last_login_info, srv->cur_login_info); buffer_copy_string(srv->cur_login_info, user->ptr); buffer_append_string(srv->cur_login_info, ">"); char srv_time[255]; strftime(srv_time, 254, "%Y/%m/%d %H:%M:%S", localtime(&(srv->cur_ts))); buffer_append_string(srv->cur_login_info, srv_time); buffer_append_string(srv->cur_login_info, ">"); buffer_append_string(srv->cur_login_info, con->dst_addr_buf->ptr); #if EMBEDDED_EANBLE nvram_set_webdav_last_login_info(srv->last_login_info->ptr); #endif } } else { //- check user / password struct stat st; if( con->smb_info && buffer_is_equal_string(con->smb_info->username, "RELOGIN", 7) ){ buffer_reset(con->smb_info->username); buffer_reset(con->smb_info->password); goto error_401; } int res = smbc_server_check_creds( con->smb_info->server->ptr, con->smb_info->share->ptr, con->smb_info->workgroup->ptr, user->ptr, pass->ptr ); //if( res == NT_STATUS_V(NT_STATUS_NOT_SUPPORTED)) { if( res == 0xc00000bb ){ buffer_free(user); buffer_free(pass); con->http_status = 406; return HANDLER_FINISHED; } else if(res != 0) { //the username/password for smb_server is not correct if(con->smb_info->username->used && con->smb_info->password->used){ buffer_copy_string_buffer(user, con->smb_info->username); buffer_copy_string_buffer(pass, con->smb_info->password); Cdbg(DBE, "Try to login again, server=%s, share=%s, user=%s, pass=%s", con->smb_info->server->ptr, con->smb_info->share->ptr, user->ptr, pass->ptr); //- MUST login again more than 30 minutes if(result>1800){ buffer_copy_string(pass, ""); } int res = smbc_server_check_creds(con->smb_info->server->ptr, con->smb_info->share->ptr, con->smb_info->workgroup->ptr, user->ptr, pass->ptr); if(res != 0) goto error_401; } else goto error_401; } if(!get_account_from_smb_info) log_sys_write(srv, "sssbss", "User", user->ptr, "login", con->smb_info->server, "from ip", con->dst_addr_buf->ptr); } con->smb_info->auth_time = time(NULL); if( !buffer_is_equal_string(user, "no", 2) && !buffer_is_equal_string(pass, "no", 2)){ buffer_copy_string_buffer(con->smb_info->username, user); buffer_copy_string_buffer(con->smb_info->password, pass); Cdbg(DBE, "save username=[%s], password=[%s], time=[%d] to con->smb_info", con->smb_info->username->ptr, con->smb_info->password->ptr, con->smb_info->auth_time); } buffer_free(user); buffer_free(pass); return HANDLER_UNSET; error_401: buffer_free(user); buffer_free(pass); if(con->smb_info) con->smb_info->auth_time = time(NULL); smbc_wrapper_response_401(srv, con); return HANDLER_FINISHED; error_455: //- Block webdav buffer_free(user); buffer_free(pass); con->http_status = 455; return HANDLER_FINISHED; }