int network_init(server *srv) { buffer *b; size_t i; network_backend_t backend; struct nb_map { network_backend_t nb; const char *name; } network_backends[] = { /* lowest id wins */ #if defined USE_LINUX_SENDFILE { NETWORK_BACKEND_LINUX_SENDFILE, "linux-sendfile" }, #endif #if defined USE_FREEBSD_SENDFILE { NETWORK_BACKEND_FREEBSD_SENDFILE, "freebsd-sendfile" }, #endif #if defined USE_SOLARIS_SENDFILEV { NETWORK_BACKEND_SOLARIS_SENDFILEV, "solaris-sendfilev" }, #endif #if defined USE_WRITEV { NETWORK_BACKEND_WRITEV, "writev" }, #endif { NETWORK_BACKEND_WRITE, "write" }, { NETWORK_BACKEND_UNSET, NULL } }; #ifdef USE_OPENSSL /* load SSL certificates */ for (i = 0; i < srv->config_context->used; i++) { specific_config *s = srv->config_storage[i]; if (buffer_is_empty(s->ssl_pemfile)) continue; #ifdef OPENSSL_NO_TLSEXT { data_config *dc = (data_config *)srv->config_context->data[i]; if (COMP_HTTP_HOST == dc->comp) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", "can't use ssl.pemfile with $HTTP[\"host\"], openssl version does not support TLS extensions"); return -1; } } #endif if (srv->ssl_is_init == 0) { SSL_load_error_strings(); SSL_library_init(); srv->ssl_is_init = 1; if (0 == RAND_status()) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", "not enough entropy in the pool"); return -1; } } if (NULL == (s->ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return -1; } if (!s->ssl_use_sslv2) { /* disable SSLv2 */ if (SSL_OP_NO_SSLv2 != SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv2)) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return -1; } } if (!buffer_is_empty(s->ssl_cipher_list)) { /* Disable support for low encryption ciphers */ if (SSL_CTX_set_cipher_list(s->ssl_ctx, s->ssl_cipher_list->ptr) != 1) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return -1; } } if (!buffer_is_empty(s->ssl_ca_file)) { if (1 != SSL_CTX_load_verify_locations(s->ssl_ctx, s->ssl_ca_file->ptr, NULL)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); return -1; } if (s->ssl_verifyclient) { STACK_OF(X509_NAME) *certs = SSL_load_client_CA_file(s->ssl_ca_file->ptr); if (!certs) { log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); } if (SSL_CTX_set_session_id_context(s->ssl_ctx, (void*) &srv, sizeof(srv)) != 1) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return -1; } SSL_CTX_set_client_CA_list(s->ssl_ctx, certs); SSL_CTX_set_verify( s->ssl_ctx, SSL_VERIFY_PEER | (s->ssl_verifyclient_enforce ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT : 0), NULL ); SSL_CTX_set_verify_depth(s->ssl_ctx, s->ssl_verifyclient_depth); } } else if (s->ssl_verifyclient) { log_error_write( srv, __FILE__, __LINE__, "s", "SSL: You specified ssl.verifyclient.activate but no ca_file" ); } if (SSL_CTX_use_certificate_file(s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); return -1; } if (SSL_CTX_use_PrivateKey_file (s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); return -1; } if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) { log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:", "Private key does not match the certificate public key, reason:", ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); return -1; } SSL_CTX_set_default_read_ahead(s->ssl_ctx, 1); SSL_CTX_set_mode(s->ssl_ctx, SSL_CTX_get_mode(s->ssl_ctx) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); # ifndef OPENSSL_NO_TLSEXT if (!SSL_CTX_set_tlsext_servername_callback(s->ssl_ctx, network_ssl_servername_callback) || !SSL_CTX_set_tlsext_servername_arg(s->ssl_ctx, srv)) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", "failed to initialize TLS servername callback, openssl library does not support TLS servername extension"); return -1; } # endif } #endif b = buffer_init(); buffer_copy_string_buffer(b, srv->srvconf.bindhost); buffer_append_string_len(b, CONST_STR_LEN(":")); buffer_append_long(b, srv->srvconf.port); if (0 != network_server_init(srv, b, srv->config_storage[0])) { return -1; } buffer_free(b); #ifdef USE_OPENSSL srv->network_ssl_backend_write = network_write_chunkqueue_openssl; #endif /* get a usefull default */ backend = network_backends[0].nb; /* match name against known types */ if (!buffer_is_empty(srv->srvconf.network_backend)) { for (i = 0; network_backends[i].name; i++) { /**/ if (buffer_is_equal_string(srv->srvconf.network_backend, network_backends[i].name, strlen(network_backends[i].name))) { backend = network_backends[i].nb; break; } } if (NULL == network_backends[i].name) { /* we don't know it */ log_error_write(srv, __FILE__, __LINE__, "sb", "server.network-backend has a unknown value:", srv->srvconf.network_backend); return -1; } } switch(backend) { case NETWORK_BACKEND_WRITE: srv->network_backend_write = network_write_chunkqueue_write; break; #ifdef USE_WRITEV case NETWORK_BACKEND_WRITEV: srv->network_backend_write = network_write_chunkqueue_writev; break; #endif #ifdef USE_LINUX_SENDFILE case NETWORK_BACKEND_LINUX_SENDFILE: srv->network_backend_write = network_write_chunkqueue_linuxsendfile; break; #endif #ifdef USE_FREEBSD_SENDFILE case NETWORK_BACKEND_FREEBSD_SENDFILE: srv->network_backend_write = network_write_chunkqueue_freebsdsendfile; break; #endif #ifdef USE_SOLARIS_SENDFILEV case NETWORK_BACKEND_SOLARIS_SENDFILEV: srv->network_backend_write = network_write_chunkqueue_solarissendfilev; break; #endif default: return -1; } /* check for $SERVER["socket"] */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; specific_config *s = srv->config_storage[i]; size_t j; /* not our stage */ if (COMP_SERVER_SOCKET != dc->comp) continue; if (dc->cond != CONFIG_COND_EQ) continue; /* check if we already know this socket, * if yes, don't init it */ for (j = 0; j < srv->srv_sockets.used; j++) { if (buffer_is_equal(srv->srv_sockets.ptr[j]->srv_token, dc->string)) { break; } } if (j == srv->srv_sockets.used) { if (0 != network_server_init(srv, dc->string, s)) return -1; } } return 0; }
static handler_t mod_status_handler(server *srv, connection *con, void *p_d) { inform_proxy1_arrival(); // Communicating arrival of a request to Proxy1 (BTP) //For verifying if token is authentic //if(verify_token((con->uri.query)->ptr) == 0) // return HANDLER_ERROR; //modified if(verify_token((con->uri.query)->ptr) == 0) { con->http_status = 404; con->file_finished = 1; return HANDLER_FINISHED; } gettimeofday(&(con->tv_temp),NULL); con->start_time = ((con->tv_temp).tv_sec)*1000000 + ((con->tv_temp).tv_usec); plugin_data *p = p_d; mod_status_patch_connection(srv, con, p); if (!buffer_is_empty(p->conf.status_url) && buffer_is_equal(p->conf.status_url, con->uri.path)) { return mod_status_handle_server_status(srv, con, p_d); } else if (!buffer_is_empty(p->conf.config_url) && buffer_is_equal(p->conf.config_url, con->uri.path)) { return mod_status_handle_server_config(srv, con, p_d); } else if (!buffer_is_empty(p->conf.statistics_url) && buffer_is_equal(p->conf.statistics_url, con->uri.path)) { return mod_status_handle_server_statistics(srv, con, p_d); } return HANDLER_GO_ON; }
static int fdt_service_insert(fdt_services *service, buffer *key, fdt_server *s_server) { size_t i; for (i = 0; i < service->used; i++) { if (buffer_is_equal(key, service->servers[i]->service_name)) { break; } } if (i == service->used) { if (service->size == 0) { service->size = 8; service->servers = malloc(service->size * sizeof(*(service->servers))); assert(service->servers); } else if (service->used == service->size) { service->size += 8; service->servers = realloc(service->servers, service->size * sizeof(*(service->servers))); assert(service->servers); } service->servers[service->used++] = s_server; } else { service->servers[i] = s_server; } return 0; }
int buffer_is_equal_string(buffer *a, const char *s, size_t b_len) { buffer b; b.ptr = (char *)s; b.used = b_len + 1; return buffer_is_equal(a, &b); }
static handler_t mod_status_handler(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; mod_status_patch_connection(srv, con, p); if (!buffer_is_empty(p->conf.status_url) && buffer_is_equal(p->conf.status_url, con->uri.path)) { return mod_status_handle_server_status(srv, con, p_d); } else if (!buffer_is_empty(p->conf.config_url) && buffer_is_equal(p->conf.config_url, con->uri.path)) { return mod_status_handle_server_config(srv, con, p_d); } else if (!buffer_is_empty(p->conf.statistics_url) && buffer_is_equal(p->conf.statistics_url, con->uri.path)) { return mod_status_handle_server_statistics(srv, con, p_d); } return HANDLER_GO_ON; }
static int check_aicloud_auth_url(server *srv, connection *con, plugin_data *p){ smb_info_t *c; int i, j, k; plugin_config *s; /* skip the first, the global context */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; s = p->config_storage[i]; /* condition didn't match */ if (!config_check_cond(srv, con, dc)) continue; /* merge config */ for (j = 0; j < dc->value->used; j++) { data_unset *du = dc->value->data[j]; if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.aicloud-auth-deny"))) { PATCH(auth_deny); } } } if(p->conf.auth_deny){ for (k = 0; k < p->conf.auth_deny->used; k++) { data_string *ds = (data_string *)p->conf.auth_deny->data[k]; if (ds->value->used == 0) continue; if (strstr(con->uri.path->ptr, ds->value->ptr)) { if(p->smb_info_list==NULL) return 0; for (c = p->smb_info_list; c; c = c->next) { if( buffer_is_empty(c->server) && buffer_is_empty(c->share) && buffer_is_equal(c->src_ip, con->dst_addr_buf) ){ if(buffer_is_empty(c->username)) return 0; else return 1; } } return 0; } } } return 1; }
static connection *connection_map_get_connection(connection_map *cm, buffer *con_id) { size_t i; for (i = 0; i < cm->used; i++) { connection_map_entry *cme = cm->ptr[i]; if (buffer_is_equal(cme->con_id, con_id)) { /* found connection */ return cme->con; } } return NULL; }
static handler_t mod_simple_vhost_docroot(server *srv, connection *con, void *p_data) { plugin_data *p = p_data; /* * cache the last successfull translation from hostname (authority) to docroot * - this saves us a stat() call * */ mod_simple_vhost_patch_connection(srv, con, p); /* build_doc_root() requires a server_root; skip module if simple-vhost.server-root is not set * or set to an empty string (especially don't cache any results!) */ if (buffer_string_is_empty(p->conf.server_root)) return HANDLER_GO_ON; if (!buffer_string_is_empty(p->conf.docroot_cache_key) && !buffer_string_is_empty(con->uri.authority) && buffer_is_equal(p->conf.docroot_cache_key, con->uri.authority)) { /* cache hit */ buffer_copy_buffer(con->server_name, p->conf.docroot_cache_servername); buffer_copy_buffer(con->physical.doc_root, p->conf.docroot_cache_value); } else { /* build document-root */ if (buffer_string_is_empty(con->uri.authority) || build_doc_root(srv, con, p, p->doc_root, con->uri.authority)) { /* not found, fallback the default-host */ if (0 == build_doc_root(srv, con, p, p->doc_root, p->conf.default_host)) { /* default host worked */ buffer_copy_buffer(con->server_name, p->conf.default_host); buffer_copy_buffer(con->physical.doc_root, p->doc_root); /* do not cache default host */ } return HANDLER_GO_ON; } /* found host */ buffer_copy_buffer(con->server_name, con->uri.authority); buffer_copy_buffer(con->physical.doc_root, p->doc_root); /* copy to cache */ buffer_copy_buffer(p->conf.docroot_cache_key, con->uri.authority); buffer_copy_buffer(p->conf.docroot_cache_value, p->doc_root); buffer_copy_buffer(p->conf.docroot_cache_servername, con->server_name); } return HANDLER_GO_ON; }
fd_data * mod_lisp_allocate_socket (plugin_data *p) { int i; fd_data *ff, *ff_open, *ff_free, *ff_stale, *ff_borrowed; ff_open = ff_free = ff_stale = ff_borrowed = NULL; for (ff = p->LispSocketPool, i = 0; i <= p->LispSocketPoolUsed && i < p->LispSocketPoolSize && ! (ff_open && ff_free && ff_stale && ff_borrowed); ++i, ++ff) { if (ff->fd >= 0) { if (! (ff->state & (FD_STATE_WRITE | FD_STATE_READ))) { if (buffer_is_equal(ff->ip, p->conf.LispServerIP) && ff->port == p->conf.LispServerPort) { /* Option 1: an open, non-stale socket to reuse */ if (!ff_open) ff_open = ff; } else { /* Option 2: the same, but the socket is open to a different Lisp */ if (!ff_borrowed) ff_borrowed = ff; } } else if ((FD_STATE_UNSAFE_EV_COUNT(ff->state) >= LISP_STALE_SOCKET_TOLERANCE)) { /* Option 3: a stale socket */ if (!ff_stale) ff_stale = ff; } } else { /* Option 4: an unallocated slot */ if (!ff_free) ff_free = ff; } } /* Allocation priority */ if (ff_open) ff = ff_open; else if (ff_free) ff = ff_free; else if (ff_stale) ff = ff_stale; else ff = ff_borrowed; if (ff != ff_open) { if (ff == ff_borrowed) FD_STATE_UNSAFE_EV_COUNT_INC(ff->state); buffer_copy_string_buffer(ff->ip, p->conf.LispServerIP); buffer_copy_string_buffer(ff->id, p->conf.LispServerId); ff->port = p->conf.LispServerPort; } if ((int) (ff - p->LispSocketPool) >= p->LispSocketPoolUsed) ++(p->LispSocketPoolUsed); return ff; }
static handler_t mod_simple_vhost_docroot(server *srv, connection *con, void *p_data) { plugin_data *p = p_data; /* * cache the last successfull translation from hostname (authority) to docroot * - this saves us a stat() call * */ mod_simple_vhost_patch_connection(srv, con, p); if (p->conf.docroot_cache_key->used && con->uri.authority->used && buffer_is_equal(p->conf.docroot_cache_key, con->uri.authority)) { /* cache hit */ buffer_copy_string_buffer(con->physical.doc_root, p->conf.docroot_cache_value); buffer_copy_string_buffer(con->server_name, p->conf.docroot_cache_servername); } else { /* build document-root */ if ((con->uri.authority->used == 0) || build_doc_root(srv, con, p, p->doc_root, con->uri.authority)) { /* not found, fallback the default-host */ if (build_doc_root(srv, con, p, p->doc_root, p->conf.default_host)) { return HANDLER_GO_ON; } else { buffer_copy_string_buffer(con->server_name, p->conf.default_host); } } else { buffer_copy_string_buffer(con->server_name, con->uri.authority); } /* copy to cache */ buffer_copy_string_buffer(p->conf.docroot_cache_key, con->uri.authority); buffer_copy_string_buffer(p->conf.docroot_cache_value, p->doc_root); buffer_copy_string_buffer(p->conf.docroot_cache_servername, con->server_name); buffer_copy_string_buffer(con->physical.doc_root, p->doc_root); } return HANDLER_GO_ON; }
int plugins_load(server *srv) { plugin *p; size_t i, j; for (i = 0; i < srv->srvconf.modules->used; i++) { data_string *d = (data_string *)srv->srvconf.modules->data[i]; char *module = d->value->ptr; for (j = 0; j < i; j++) { if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "Cannot load plugin", d->value, "more than once, please fix your config (lighttpd may not accept such configs in future releases)"); continue; } } for (j = 0; load_functions[j].name; ++j) { if (0 == strcmp(load_functions[j].name, module)) { p = plugin_init(); if ((*load_functions[j].plugin_init)(p)) { log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" ); plugin_free(p); return -1; } plugins_register(srv, p); break; } } if (!load_functions[j].name) { log_error_write(srv, __FILE__, __LINE__, "ss", module, " plugin not found" ); return -1; } } return 0; }
int network_init(server *srv) { buffer *b; size_t i, j; network_backend_t backend; #if OPENSSL_VERSION_NUMBER >= 0x0090800fL #ifndef OPENSSL_NO_ECDH EC_KEY *ecdh; int nid; #endif #endif #ifdef USE_OPENSSL # ifndef OPENSSL_NO_DH DH *dh; # endif BIO *bio; /* 1024-bit MODP Group with 160-bit prime order subgroup (RFC5114) * -----BEGIN DH PARAMETERS----- * MIIBDAKBgQCxC4+WoIDgHd6S3l6uXVTsUsmfvPsGo8aaap3KUtI7YWBz4oZ1oj0Y * mDjvHi7mUsAT7LSuqQYRIySXXDzUm4O/rMvdfZDEvXCYSI6cIZpzck7/1vrlZEc4 * +qMaT/VbzMChUa9fDci0vUW/N982XBpl5oz9p21NpwjfH7K8LkpDcQKBgQCk0cvV * w/00EmdlpELvuZkF+BBN0lisUH/WQGz/FCZtMSZv6h5cQVZLd35pD1UE8hMWAhe0 * sBuIal6RVH+eJ0n01/vX07mpLuGQnQ0iY/gKdqaiTAh6CR9THb8KAWm2oorWYqTR * jnOvoy13nVkY0IvIhY9Nzvl8KiSFXm7rIrOy5QICAKA= * -----END DH PARAMETERS----- */ static const unsigned char dh1024_p[]={ 0xB1,0x0B,0x8F,0x96,0xA0,0x80,0xE0,0x1D,0xDE,0x92,0xDE,0x5E, 0xAE,0x5D,0x54,0xEC,0x52,0xC9,0x9F,0xBC,0xFB,0x06,0xA3,0xC6, 0x9A,0x6A,0x9D,0xCA,0x52,0xD2,0x3B,0x61,0x60,0x73,0xE2,0x86, 0x75,0xA2,0x3D,0x18,0x98,0x38,0xEF,0x1E,0x2E,0xE6,0x52,0xC0, 0x13,0xEC,0xB4,0xAE,0xA9,0x06,0x11,0x23,0x24,0x97,0x5C,0x3C, 0xD4,0x9B,0x83,0xBF,0xAC,0xCB,0xDD,0x7D,0x90,0xC4,0xBD,0x70, 0x98,0x48,0x8E,0x9C,0x21,0x9A,0x73,0x72,0x4E,0xFF,0xD6,0xFA, 0xE5,0x64,0x47,0x38,0xFA,0xA3,0x1A,0x4F,0xF5,0x5B,0xCC,0xC0, 0xA1,0x51,0xAF,0x5F,0x0D,0xC8,0xB4,0xBD,0x45,0xBF,0x37,0xDF, 0x36,0x5C,0x1A,0x65,0xE6,0x8C,0xFD,0xA7,0x6D,0x4D,0xA7,0x08, 0xDF,0x1F,0xB2,0xBC,0x2E,0x4A,0x43,0x71, }; static const unsigned char dh1024_g[]={ 0xA4,0xD1,0xCB,0xD5,0xC3,0xFD,0x34,0x12,0x67,0x65,0xA4,0x42, 0xEF,0xB9,0x99,0x05,0xF8,0x10,0x4D,0xD2,0x58,0xAC,0x50,0x7F, 0xD6,0x40,0x6C,0xFF,0x14,0x26,0x6D,0x31,0x26,0x6F,0xEA,0x1E, 0x5C,0x41,0x56,0x4B,0x77,0x7E,0x69,0x0F,0x55,0x04,0xF2,0x13, 0x16,0x02,0x17,0xB4,0xB0,0x1B,0x88,0x6A,0x5E,0x91,0x54,0x7F, 0x9E,0x27,0x49,0xF4,0xD7,0xFB,0xD7,0xD3,0xB9,0xA9,0x2E,0xE1, 0x90,0x9D,0x0D,0x22,0x63,0xF8,0x0A,0x76,0xA6,0xA2,0x4C,0x08, 0x7A,0x09,0x1F,0x53,0x1D,0xBF,0x0A,0x01,0x69,0xB6,0xA2,0x8A, 0xD6,0x62,0xA4,0xD1,0x8E,0x73,0xAF,0xA3,0x2D,0x77,0x9D,0x59, 0x18,0xD0,0x8B,0xC8,0x85,0x8F,0x4D,0xCE,0xF9,0x7C,0x2A,0x24, 0x85,0x5E,0x6E,0xEB,0x22,0xB3,0xB2,0xE5, }; #endif struct nb_map { network_backend_t nb; const char *name; } network_backends[] = { /* lowest id wins */ #if defined USE_SENDFILE { NETWORK_BACKEND_SENDFILE, "sendfile" }, #endif #if defined USE_LINUX_SENDFILE { NETWORK_BACKEND_SENDFILE, "linux-sendfile" }, #endif #if defined USE_FREEBSD_SENDFILE { NETWORK_BACKEND_SENDFILE, "freebsd-sendfile" }, #endif #if defined USE_SOLARIS_SENDFILEV { NETWORK_BACKEND_SENDFILE, "solaris-sendfilev" }, #endif #if defined USE_WRITEV { NETWORK_BACKEND_WRITEV, "writev" }, #endif { NETWORK_BACKEND_WRITE, "write" }, { NETWORK_BACKEND_UNSET, NULL } }; #ifdef USE_OPENSSL /* load SSL certificates */ for (i = 0; i < srv->config_context->used; i++) { specific_config *s = srv->config_storage[i]; #ifndef SSL_OP_NO_COMPRESSION # define SSL_OP_NO_COMPRESSION 0 #endif long ssloptions = SSL_OP_ALL | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_NO_COMPRESSION; if (buffer_string_is_empty(s->ssl_pemfile) && buffer_string_is_empty(s->ssl_ca_file)) continue; if (srv->ssl_is_init == 0) { SSL_load_error_strings(); SSL_library_init(); OpenSSL_add_all_algorithms(); srv->ssl_is_init = 1; if (0 == RAND_status()) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", "not enough entropy in the pool"); return -1; } } if (!buffer_string_is_empty(s->ssl_pemfile)) { #ifdef OPENSSL_NO_TLSEXT data_config *dc = (data_config *)srv->config_context->data[i]; if (COMP_HTTP_HOST == dc->comp) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", "can't use ssl.pemfile with $HTTP[\"host\"], openssl version does not support TLS extensions"); return -1; } #endif if (network_openssl_load_pemfile(srv, i)) return -1; } if (!buffer_string_is_empty(s->ssl_ca_file)) { s->ssl_ca_file_cert_names = SSL_load_client_CA_file(s->ssl_ca_file->ptr); if (NULL == s->ssl_ca_file_cert_names) { log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); } } if (buffer_string_is_empty(s->ssl_pemfile) || !s->ssl_enabled) continue; if (NULL == (s->ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return -1; } /* completely useless identifier; required for client cert verification to work with sessions */ if (0 == SSL_CTX_set_session_id_context(s->ssl_ctx, (const unsigned char*) CONST_STR_LEN("lighttpd"))) { log_error_write(srv, __FILE__, __LINE__, "ss:s", "SSL:", "failed to set session context", ERR_error_string(ERR_get_error(), NULL)); return -1; } if (s->ssl_empty_fragments) { #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS ssloptions &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; #else ssloptions &= ~0x00000800L; /* hardcode constant */ log_error_write(srv, __FILE__, __LINE__, "ss", "WARNING: SSL:", "'insert empty fragments' not supported by the openssl version used to compile lighttpd with"); #endif } SSL_CTX_set_options(s->ssl_ctx, ssloptions); SSL_CTX_set_info_callback(s->ssl_ctx, ssl_info_callback); if (!s->ssl_use_sslv2) { /* disable SSLv2 */ if ((SSL_OP_NO_SSLv2 & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv2)) != SSL_OP_NO_SSLv2) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return -1; } } if (!s->ssl_use_sslv3) { /* disable SSLv3 */ if ((SSL_OP_NO_SSLv3 & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv3)) != SSL_OP_NO_SSLv3) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return -1; } } if (!buffer_string_is_empty(s->ssl_cipher_list)) { /* Disable support for low encryption ciphers */ if (SSL_CTX_set_cipher_list(s->ssl_ctx, s->ssl_cipher_list->ptr) != 1) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", ERR_error_string(ERR_get_error(), NULL)); return -1; } if (s->ssl_honor_cipher_order) { SSL_CTX_set_options(s->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); } } #ifndef OPENSSL_NO_DH /* Support for Diffie-Hellman key exchange */ if (!buffer_string_is_empty(s->ssl_dh_file)) { /* DH parameters from file */ bio = BIO_new_file((char *) s->ssl_dh_file->ptr, "r"); if (bio == NULL) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: Unable to open file", s->ssl_dh_file->ptr); return -1; } dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); if (dh == NULL) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: PEM_read_bio_DHparams failed", s->ssl_dh_file->ptr); return -1; } } else { BIGNUM *dh_p, *dh_g; /* Default DH parameters from RFC5114 */ dh = DH_new(); if (dh == NULL) { log_error_write(srv, __FILE__, __LINE__, "s", "SSL: DH_new () failed"); return -1; } dh_p = BN_bin2bn(dh1024_p,sizeof(dh1024_p), NULL); dh_g = BN_bin2bn(dh1024_g,sizeof(dh1024_g), NULL); if ((dh_p == NULL) || (dh_g == NULL)) { DH_free(dh); log_error_write(srv, __FILE__, __LINE__, "s", "SSL: BN_bin2bn () failed"); return -1; } #if OPENSSL_VERSION_NUMBER < 0x10100000L \ || defined(LIBRESSL_VERSION_NUMBER) dh->p = dh_p; dh->g = dh_g; dh->length = 160; #else DH_set0_pqg(dh, dh_p, NULL, dh_g); DH_set_length(dh, 160); #endif } SSL_CTX_set_tmp_dh(s->ssl_ctx,dh); SSL_CTX_set_options(s->ssl_ctx,SSL_OP_SINGLE_DH_USE); DH_free(dh); #else if (!buffer_string_is_empty(s->ssl_dh_file)) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: openssl compiled without DH support, can't load parameters from", s->ssl_dh_file->ptr); } #endif #if OPENSSL_VERSION_NUMBER >= 0x0090800fL #ifndef OPENSSL_NO_ECDH /* Support for Elliptic-Curve Diffie-Hellman key exchange */ if (!buffer_string_is_empty(s->ssl_ec_curve)) { /* OpenSSL only supports the "named curves" from RFC 4492, section 5.1.1. */ nid = OBJ_sn2nid((char *) s->ssl_ec_curve->ptr); if (nid == 0) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: Unknown curve name", s->ssl_ec_curve->ptr); return -1; } } else { /* Default curve */ nid = OBJ_sn2nid("prime256v1"); } ecdh = EC_KEY_new_by_curve_name(nid); if (ecdh == NULL) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL: Unable to create curve", s->ssl_ec_curve->ptr); return -1; } SSL_CTX_set_tmp_ecdh(s->ssl_ctx,ecdh); SSL_CTX_set_options(s->ssl_ctx,SSL_OP_SINGLE_ECDH_USE); EC_KEY_free(ecdh); #endif #endif /* load all ssl.ca-files specified in the config into each SSL_CTX to be prepared for SNI */ for (j = 0; j < srv->config_context->used; j++) { specific_config *s1 = srv->config_storage[j]; if (!buffer_string_is_empty(s1->ssl_ca_file)) { if (1 != SSL_CTX_load_verify_locations(s->ssl_ctx, s1->ssl_ca_file->ptr, NULL)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", ERR_error_string(ERR_get_error(), NULL), s1->ssl_ca_file); return -1; } } } if (s->ssl_verifyclient) { if (NULL == s->ssl_ca_file_cert_names) { log_error_write(srv, __FILE__, __LINE__, "s", "SSL: You specified ssl.verifyclient.activate but no ca_file" ); return -1; } SSL_CTX_set_client_CA_list(s->ssl_ctx, SSL_dup_CA_list(s->ssl_ca_file_cert_names)); SSL_CTX_set_verify( s->ssl_ctx, SSL_VERIFY_PEER | (s->ssl_verifyclient_enforce ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT : 0), NULL ); SSL_CTX_set_verify_depth(s->ssl_ctx, s->ssl_verifyclient_depth); } if (SSL_CTX_use_certificate(s->ssl_ctx, s->ssl_pemfile_x509) < 0) { log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); return -1; } if (SSL_CTX_use_PrivateKey(s->ssl_ctx, s->ssl_pemfile_pkey) < 0) { log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); return -1; } if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) { log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:", "Private key does not match the certificate public key, reason:", ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); return -1; } SSL_CTX_set_default_read_ahead(s->ssl_ctx, 1); SSL_CTX_set_mode(s->ssl_ctx, SSL_CTX_get_mode(s->ssl_ctx) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); # ifndef OPENSSL_NO_TLSEXT if (!SSL_CTX_set_tlsext_servername_callback(s->ssl_ctx, network_ssl_servername_callback) || !SSL_CTX_set_tlsext_servername_arg(s->ssl_ctx, srv)) { log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", "failed to initialize TLS servername callback, openssl library does not support TLS servername extension"); return -1; } # endif } #endif b = buffer_init(); buffer_copy_buffer(b, srv->srvconf.bindhost); buffer_append_string_len(b, CONST_STR_LEN(":")); buffer_append_int(b, srv->srvconf.port); if (0 != network_server_init(srv, b, srv->config_storage[0])) { buffer_free(b); return -1; } buffer_free(b); #ifdef USE_OPENSSL srv->network_ssl_backend_write = network_write_chunkqueue_openssl; #endif /* get a usefull default */ backend = network_backends[0].nb; /* match name against known types */ if (!buffer_string_is_empty(srv->srvconf.network_backend)) { for (i = 0; network_backends[i].name; i++) { /**/ if (buffer_is_equal_string(srv->srvconf.network_backend, network_backends[i].name, strlen(network_backends[i].name))) { backend = network_backends[i].nb; break; } } if (NULL == network_backends[i].name) { /* we don't know it */ log_error_write(srv, __FILE__, __LINE__, "sb", "server.network-backend has a unknown value:", srv->srvconf.network_backend); return -1; } } switch(backend) { case NETWORK_BACKEND_WRITE: srv->network_backend_write = network_write_chunkqueue_write; break; #if defined(USE_WRITEV) case NETWORK_BACKEND_WRITEV: srv->network_backend_write = network_write_chunkqueue_writev; break; #endif #if defined(USE_SENDFILE) case NETWORK_BACKEND_SENDFILE: srv->network_backend_write = network_write_chunkqueue_sendfile; break; #endif default: return -1; } /* check for $SERVER["socket"] */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; specific_config *s = srv->config_storage[i]; /* not our stage */ if (COMP_SERVER_SOCKET != dc->comp) continue; if (dc->cond != CONFIG_COND_EQ) continue; /* check if we already know this socket, * if yes, don't init it */ for (j = 0; j < srv->srv_sockets.used; j++) { if (buffer_is_equal(srv->srv_sockets.ptr[j]->srv_token, dc->string)) { break; } } if (j == srv->srv_sockets.used) { if (0 != network_server_init(srv, dc->string, s)) return -1; } } return 0; }
int config_set_defaults(server *srv) { specific_config *s = srv->config_storage[0]; const fdevent_handler_info_t *handler; const network_backend_info_t *backend; struct stat st1, st2; if (buffer_is_empty(s->document_root)) { log_error_write(srv, __FILE__, __LINE__, "s", "a default document-root has to be set"); return -1; } if (buffer_is_empty(srv->srvconf.changeroot)) { pathname_unix2local(s->document_root); if (-1 == stat(s->document_root->ptr, &st1)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "base-docroot doesn't exist:", s->document_root, strerror(errno)); return -1; } } else { buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.changeroot); buffer_append_string_buffer(srv->tmp_buf, s->document_root); if (-1 == stat(srv->tmp_buf->ptr, &st1)) { log_error_write(srv, __FILE__, __LINE__, "sb", "base-docroot doesn't exist:", srv->tmp_buf); return -1; } } buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_lower(srv->tmp_buf); if (0 == stat(srv->tmp_buf->ptr, &st1)) { int is_lower = 0; is_lower = buffer_is_equal(srv->tmp_buf, s->document_root); /* lower-case existed, check upper-case */ buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_upper(srv->tmp_buf); /* we have to handle the special case that upper and lower-casing results in the same filename * as in server.document-root = "/" or "/12345/" */ if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) { /* lower-casing and upper-casing didn't result in * an other filename, no need to stat(), * just assume it is case-sensitive. */ s->force_lowercase_filenames = 0; } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { /* upper case exists too, doesn't the FS handle this ? */ /* upper and lower have the same inode -> case-insensitve FS */ if (st1.st_ino == st2.st_ino) { /* upper and lower have the same inode -> case-insensitve FS */ s->force_lowercase_filenames = 1; } } } if (srv->srvconf.port == 0) { srv->srvconf.port = s->is_ssl ? 443 : 80; } if (srv->srvconf.event_handler->used == 0) { /* get a useful default */ handler = fdevent_get_defaulthandler(); if (!handler) { log_error_write(srv, __FILE__, __LINE__, "s", "sorry, there is no event handler for this system"); return -1; } } else { /* * User override */ handler = fdevent_get_handler_info_by_name(srv->srvconf.event_handler->ptr); if (!handler) { log_error_write(srv, __FILE__, __LINE__, "sb", "the selected event-handler is unknown:", srv->srvconf.event_handler ); return -1; } if (!handler->init) { log_error_write(srv, __FILE__, __LINE__, "sb", "the selected event-handler is known but not supported:", srv->srvconf.event_handler ); return -1; } } srv->event_handler = handler->type; if (buffer_is_empty(srv->srvconf.network_backend)) { /* get a useful default */ backend = network_get_defaultbackend(); if (!backend) { log_error_write(srv, __FILE__, __LINE__, "s", "sorry, there is no network backend for this system"); return -1; } } else { /* * User override */ backend = network_get_backend_info_by_name(srv->srvconf.network_backend->ptr); if (!backend) { /* we don't know it */ log_error_write(srv, __FILE__, __LINE__, "sb", "server.network-backend has a unknown value:", srv->srvconf.network_backend); return -1; } if (backend->write_handler == NULL) { /* we know it but not supported */ log_error_write(srv, __FILE__, __LINE__, "sb", "server.network-backend not supported:", srv->srvconf.network_backend); return -1; } } srv->network_backend = backend->type; if (s->is_ssl) { if (buffer_is_empty(s->ssl_pemfile)) { /* PEM file is require */ log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); return -1; } #ifndef USE_OPENSSL log_error_write(srv, __FILE__, __LINE__, "s", "ssl support is missing, recompile with --with-openssl"); return -1; #endif } return 0; }
static void https_add_ssl_entries(connection *con) { X509 *xs; X509_NAME *xn; X509_NAME_ENTRY *xe; int i, nentries; if ( SSL_get_verify_result(con->ssl) != X509_V_OK || !(xs = SSL_get_peer_certificate(con->ssl)) ) { return; } xn = X509_get_subject_name(xs); for (i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) { int xobjnid; const char * xobjsn; data_string *envds; if (!(xe = X509_NAME_get_entry(xn, i))) { continue; } xobjnid = OBJ_obj2nid((ASN1_OBJECT*)X509_NAME_ENTRY_get_object(xe)); xobjsn = OBJ_nid2sn(xobjnid); if (!xobjsn) { continue; } if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { envds = data_string_init(); } buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_S_DN_")); buffer_append_string(envds->key, xobjsn); buffer_copy_string_len( envds->value, (const char *)xe->value->data, xe->value->length ); /* pick one of the exported values as "authed user", for example * ssl.verifyclient.username = "******" or "SSL_CLIENT_S_DN_emailAddress" */ if (buffer_is_equal(con->conf.ssl_verifyclient_username, envds->key)) { buffer_copy_string_buffer(con->authed_user, envds->value); } array_insert_unique(con->environment, (data_unset *)envds); } if (con->conf.ssl_verifyclient_export_cert) { BIO *bio; if (NULL != (bio = BIO_new(BIO_s_mem()))) { data_string *envds; int n; PEM_write_bio_X509(bio, xs); n = BIO_pending(bio); if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { envds = data_string_init(); } buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_CERT")); buffer_prepare_copy(envds->value, n+1); BIO_read(bio, envds->value->ptr, n); BIO_free(bio); envds->value->ptr[n] = '\0'; envds->value->used = n+1; array_insert_unique(con->environment, (data_unset *)envds); } } X509_free(xs); }
handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) { #ifdef HAVE_FAM_H fam_dir_entry *fam_dir = NULL; int dir_ndx = -1; splay_tree *dir_node = NULL; #endif stat_cache_entry *sce = NULL; stat_cache *sc; struct stat st; size_t k; int fd; struct stat lst; #ifdef DEBUG_STAT_CACHE size_t i; #endif int file_ndx; splay_tree *file_node = NULL; *ret_sce = NULL; /* * check if the directory for this file has changed */ sc = srv->stat_cache; buffer_copy_buffer(sc->hash_key, name); buffer_append_int(sc->hash_key, con->conf.follow_symlink); file_ndx = hashme(sc->hash_key); sc->files = splaytree_splay(sc->files, file_ndx); #ifdef DEBUG_STAT_CACHE for (i = 0; i < ctrl.used; i++) { if (ctrl.ptr[i] == file_ndx) break; } #endif if (sc->files && (sc->files->key == file_ndx)) { #ifdef DEBUG_STAT_CACHE /* it was in the cache */ force_assert(i < ctrl.used); #endif /* we have seen this file already and * don't stat() it again in the same second */ file_node = sc->files; sce = file_node->data; /* check if the name is the same, we might have a collision */ if (buffer_is_equal(name, sce->name)) { if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { if (sce->stat_ts == srv->cur_ts) { *ret_sce = sce; return HANDLER_GO_ON; } } } else { /* oops, a collision, * * file_node is used by the FAM check below to see if we know this file * and if we can save a stat(). * * BUT, the sce is not reset here as the entry into the cache is ok, we * it is just not pointing to our requested file. * * */ file_node = NULL; } } else { #ifdef DEBUG_STAT_CACHE if (i != ctrl.used) { log_error_write(srv, __FILE__, __LINE__, "xSB", file_ndx, "was already inserted but not found in cache, ", name); } force_assert(i == ctrl.used); #endif } #ifdef HAVE_FAM_H /* dir-check */ if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { if (0 != buffer_copy_dirname(sc->dir_name, name)) { log_error_write(srv, __FILE__, __LINE__, "sb", "no '/' found in filename:", name); return HANDLER_ERROR; } buffer_copy_buffer(sc->hash_key, sc->dir_name); buffer_append_int(sc->hash_key, con->conf.follow_symlink); dir_ndx = hashme(sc->hash_key); sc->dirs = splaytree_splay(sc->dirs, dir_ndx); if (sc->dirs && (sc->dirs->key == dir_ndx)) { dir_node = sc->dirs; } if (dir_node && file_node) { /* we found a file */ sce = file_node->data; fam_dir = dir_node->data; if (fam_dir->version == sce->dir_version) { /* the stat()-cache entry is still ok */ *ret_sce = sce; return HANDLER_GO_ON; } } } #endif /* * *lol* * - open() + fstat() on a named-pipe results in a (intended) hang. * - stat() if regular file + open() to see if we can read from it is better * * */ if (-1 == stat(name->ptr, &st)) { return HANDLER_ERROR; } if (S_ISREG(st.st_mode)) { /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */ if (name->ptr[buffer_string_length(name) - 1] == '/') { errno = ENOTDIR; return HANDLER_ERROR; } /* try to open the file to check if we can read it */ if (-1 == (fd = open(name->ptr, O_RDONLY))) { return HANDLER_ERROR; } close(fd); } if (NULL == sce) { #ifdef DEBUG_STAT_CACHE int osize = splaytree_size(sc->files); #endif sce = stat_cache_entry_init(); buffer_copy_buffer(sce->name, name); sc->files = splaytree_insert(sc->files, file_ndx, sce); #ifdef DEBUG_STAT_CACHE if (ctrl.size == 0) { ctrl.size = 16; ctrl.used = 0; ctrl.ptr = malloc(ctrl.size * sizeof(*ctrl.ptr)); } else if (ctrl.size == ctrl.used) { ctrl.size += 16; ctrl.ptr = realloc(ctrl.ptr, ctrl.size * sizeof(*ctrl.ptr)); } ctrl.ptr[ctrl.used++] = file_ndx; force_assert(sc->files); force_assert(sc->files->data == sce); force_assert(osize + 1 == splaytree_size(sc->files)); #endif } sce->st = st; sce->stat_ts = srv->cur_ts; /* catch the obvious symlinks * * this is not a secure check as we still have a race-condition between * the stat() and the open. We can only solve this by * 1. open() the file * 2. fstat() the fd * * and keeping the file open for the rest of the time. But this can * only be done at network level. * * per default it is not a symlink * */ #ifdef HAVE_LSTAT sce->is_symlink = 0; /* we want to only check for symlinks if we should block symlinks. */ if (!con->conf.follow_symlink) { if (stat_cache_lstat(srv, name, &lst) == 0) { #ifdef DEBUG_STAT_CACHE log_error_write(srv, __FILE__, __LINE__, "sb", "found symlink", name); #endif sce->is_symlink = 1; } /* * we assume "/" can not be symlink, so * skip the symlink stuff if our path is / **/ else if (buffer_string_length(name) > 1) { buffer *dname; char *s_cur; dname = buffer_init(); buffer_copy_buffer(dname, name); while ((s_cur = strrchr(dname->ptr, '/'))) { buffer_string_set_length(dname, s_cur - dname->ptr); if (dname->ptr == s_cur) { #ifdef DEBUG_STAT_CACHE log_error_write(srv, __FILE__, __LINE__, "s", "reached /"); #endif break; } #ifdef DEBUG_STAT_CACHE log_error_write(srv, __FILE__, __LINE__, "sbs", "checking if", dname, "is a symlink"); #endif if (stat_cache_lstat(srv, dname, &lst) == 0) { sce->is_symlink = 1; #ifdef DEBUG_STAT_CACHE log_error_write(srv, __FILE__, __LINE__, "sb", "found symlink", dname); #endif break; }; }; buffer_free(dname); }; }; #endif if (S_ISREG(st.st_mode)) { /* determine mimetype */ buffer_reset(sce->content_type); #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) if (con->conf.use_xattr) { stat_cache_attr_get(sce->content_type, name->ptr); } #endif /* xattr did not set a content-type. ask the config */ if (buffer_string_is_empty(sce->content_type)) { size_t namelen = buffer_string_length(name); for (k = 0; k < con->conf.mimetypes->used; k++) { data_string *ds = (data_string *)con->conf.mimetypes->data[k]; buffer *type = ds->key; size_t typelen = buffer_string_length(type); if (buffer_is_empty(type)) continue; /* check if the right side is the same */ if (typelen > namelen) continue; if (0 == strncasecmp(name->ptr + namelen - typelen, type->ptr, typelen)) { buffer_copy_buffer(sce->content_type, ds->value); break; } } } etag_create(sce->etag, &(sce->st), con->etag_flags); } else if (S_ISDIR(st.st_mode)) { etag_create(sce->etag, &(sce->st), con->etag_flags); } #ifdef HAVE_FAM_H if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { /* is this directory already registered ? */ if (!dir_node) { fam_dir = fam_dir_entry_init(); buffer_copy_buffer(fam_dir->name, sc->dir_name); fam_dir->version = 1; fam_dir->req = calloc(1, sizeof(FAMRequest)); if (0 != FAMMonitorDirectory(&sc->fam, fam_dir->name->ptr, fam_dir->req, fam_dir)) { log_error_write(srv, __FILE__, __LINE__, "sbsbs", "monitoring dir failed:", fam_dir->name, "file:", name, FamErrlist[FAMErrno]); fam_dir_entry_free(&sc->fam, fam_dir); fam_dir = NULL; } else { int osize = 0; if (sc->dirs) { osize = sc->dirs->size; } sc->dirs = splaytree_insert(sc->dirs, dir_ndx, fam_dir); force_assert(sc->dirs); force_assert(sc->dirs->data == fam_dir); force_assert(osize == (sc->dirs->size - 1)); } } else { fam_dir = dir_node->data; } /* bind the fam_fc to the stat() cache entry */ if (fam_dir) { sce->dir_version = fam_dir->version; } } #endif *ret_sce = sce; return HANDLER_GO_ON; }
int network_init(server *srv) { buffer *b; size_t i; network_backend_t backend; struct nb_map { network_backend_t nb; const char *name; } network_backends[] = { /* lowest id wins */ #if defined USE_LINUX_SENDFILE { NETWORK_BACKEND_LINUX_SENDFILE, "linux-sendfile" }, #endif #if defined USE_FREEBSD_SENDFILE { NETWORK_BACKEND_FREEBSD_SENDFILE, "freebsd-sendfile" }, #endif #if defined USE_SOLARIS_SENDFILEV { NETWORK_BACKEND_SOLARIS_SENDFILEV, "solaris-sendfilev" }, #endif #if defined USE_WRITEV { NETWORK_BACKEND_WRITEV, "writev" }, #endif { NETWORK_BACKEND_WRITE, "write" }, { NETWORK_BACKEND_UNSET, NULL } }; b = buffer_init(); buffer_copy_string_buffer(b, srv->srvconf.bindhost); buffer_append_string_len(b, CONST_STR_LEN(":")); buffer_append_long(b, srv->srvconf.port); if (0 != network_server_init(srv, b, srv->config_storage[0])) { return -1; } buffer_free(b); #ifdef USE_OPENSSL srv->network_ssl_backend_write = network_write_chunkqueue_openssl; #endif /* get a usefull default */ backend = network_backends[0].nb; /* match name against known types */ if (!buffer_is_empty(srv->srvconf.network_backend)) { for (i = 0; network_backends[i].name; i++) { /**/ if (buffer_is_equal_string(srv->srvconf.network_backend, network_backends[i].name, strlen(network_backends[i].name))) { backend = network_backends[i].nb; break; } } if (NULL == network_backends[i].name) { /* we don't know it */ log_error_write(srv, __FILE__, __LINE__, "sb", "server.network-backend has a unknown value:", srv->srvconf.network_backend); return -1; } } switch(backend) { case NETWORK_BACKEND_WRITE: srv->network_backend_write = network_write_chunkqueue_write; break; #ifdef USE_WRITEV case NETWORK_BACKEND_WRITEV: srv->network_backend_write = network_write_chunkqueue_writev; break; #endif #ifdef USE_LINUX_SENDFILE case NETWORK_BACKEND_LINUX_SENDFILE: srv->network_backend_write = network_write_chunkqueue_linuxsendfile; break; #endif #ifdef USE_FREEBSD_SENDFILE case NETWORK_BACKEND_FREEBSD_SENDFILE: srv->network_backend_write = network_write_chunkqueue_freebsdsendfile; break; #endif #ifdef USE_SOLARIS_SENDFILEV case NETWORK_BACKEND_SOLARIS_SENDFILEV: srv->network_backend_write = network_write_chunkqueue_solarissendfilev; break; #endif default: return -1; } /* check for $SERVER["socket"] */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; specific_config *s = srv->config_storage[i]; size_t j; /* not our stage */ if (COMP_SERVER_SOCKET != dc->comp) continue; if (dc->cond != CONFIG_COND_EQ) { log_error_write(srv, __FILE__, __LINE__, "s", "only == is allowed for $SERVER[\"socket\"]."); return -1; } /* check if we already know this socket, * if yes, don't init it */ for (j = 0; j < srv->srv_sockets.used; j++) { if (buffer_is_equal(srv->srv_sockets.ptr[j]->srv_token, dc->string)) { break; } } if (j == srv->srv_sockets.used) { if (0 != network_server_init(srv, dc->string, s)) return -1; } } 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; }
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 plugins_load(server *srv) { plugin *p; int (*init)(plugin *pl); const char *error; size_t i, j; for (i = 0; i < srv->srvconf.modules->used; i++) { data_string *d = (data_string *)srv->srvconf.modules->data[i]; char *modules = d->value->ptr; for (j = 0; j < i; j++) { if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) { log_error_write(srv, __FILE__, __LINE__, "sbs", "Cannot load plugin", d->value, "more than once, please fix your config (we may not accept such configs in future releases"); continue; } } buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.modules_dir); buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/")); buffer_append_string(srv->tmp_buf, modules); #if defined(__WIN32) || defined(__CYGWIN__) buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".dll")); #else buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".so")); #endif p = plugin_init(); #ifdef __WIN32 if (NULL == (p->lib = LoadLibrary(srv->tmp_buf->ptr))) { LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); log_error_write(srv, __FILE__, __LINE__, "ssb", "LoadLibrary() failed", lpMsgBuf, srv->tmp_buf); plugin_free(p); return -1; } #else if (NULL == (p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW|RTLD_GLOBAL))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:", srv->tmp_buf, dlerror()); plugin_free(p); return -1; } #endif buffer_reset(srv->tmp_buf); buffer_copy_string(srv->tmp_buf, modules); buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init")); #ifdef __WIN32 init = GetProcAddress(p->lib, srv->tmp_buf->ptr); if (init == NULL) { LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); log_error_write(srv, __FILE__, __LINE__, "sbs", "getprocaddress failed:", srv->tmp_buf, lpMsgBuf); plugin_free(p); return -1; } #else #if 1 init = (int (*)(plugin *))(intptr_t)dlsym(p->lib, srv->tmp_buf->ptr); #else *(void **)(&init) = dlsym(p->lib, srv->tmp_buf->ptr); #endif if ((error = dlerror()) != NULL) { log_error_write(srv, __FILE__, __LINE__, "s", error); plugin_free(p); return -1; } #endif if ((*init)(p)) { log_error_write(srv, __FILE__, __LINE__, "ss", modules, "plugin init failed" ); plugin_free(p); return -1; } #if 0 log_error_write(srv, __FILE__, __LINE__, "ss", modules, "plugin loaded" ); #endif plugins_register(srv, p); } return 0; }
//获取那些创建监听套接字必须的用户配置信息,并调用network_server_init函数创建监听套接字描述符 int network_init(server *srv) { buffer *b; size_t i; //记录采用的读写方式 network_backend_t backend; //类似于之前讲过得I/O 复用技术的选择,这里Lighttpd也按照所谓的优劣次序选择服务器向客户端 发送 数据的方式。 //这些方式包括有所谓的“零拷贝”方式(这是目前性能最优的网络数据传递方式)、散布读/聚集写方式以及一般的读写方式。 struct nb_map { network_backend_t nb; const char *name; } network_backends[] = { /* lowest id wins */ #if defined USE_LINUX_SENDFILE { NETWORK_BACKEND_LINUX_SENDFILE, "linux-sendfile" }, #endif #if defined USE_FREEBSD_SENDFILE { NETWORK_BACKEND_FREEBSD_SENDFILE, "freebsd-sendfile" }, #endif #if defined USE_SOLARIS_SENDFILEV { NETWORK_BACKEND_SOLARIS_SENDFILEV, "solaris-sendfilev" }, #endif #if defined USE_WRITEV { NETWORK_BACKEND_WRITEV, "writev" }, #endif { NETWORK_BACKEND_WRITE, "write" }, { NETWORK_BACKEND_UNSET, NULL } }; //#part1 //创建主Web站点的监听套接口描述符,绑定的IP地址由配置server.bind指定,端口又配置项server.port指定。 //如果用户未指定配置项,server.bind则自动绑定到通配地址INADDR_ANY,而配置项server.port未指定则默认为HTTP常规端口80。 b = buffer_init(); //从用户配置信息中获取用于创建监听套接字的ip和port buffer_copy_string_buffer(b, srv->srvconf.bindhost); buffer_append_string_len(b, CONST_STR_LEN(":")); buffer_append_long(b, srv->srvconf.port); //调用函数network_server_init()实际创建监听套接口描述符。 if (0 != network_server_init(srv, b, srv->config_storage[0])) { return -1; } buffer_free(b); //#part1 #ifdef USE_OPENSSL srv->network_ssl_backend_write = network_write_chunkqueue_openssl; #endif //默认选择系统支持的最好的数据读写方式 backend = network_backends[0].nb; //用户实际选择数据读写方式 if (!buffer_is_empty(srv->srvconf.network_backend)) { for (i = 0; network_backends[i].name; i++) { /**/ if (buffer_is_equal_string(srv->srvconf.network_backend, network_backends[i].name, strlen(network_backends[i].name))) { backend = network_backends[i].nb; break; } } //用户选择了一个无效的读写方式,则记录错误,并程序退出 if (NULL == network_backends[i].name) { /* we don't know it */ log_error_write(srv, __FILE__, __LINE__, "sb", "server.network-backend has a unknown value:", srv->srvconf.network_backend); return -1; } } //根据最终选择的数据读写方式来发送数据,关联回调函数 switch(backend) { case NETWORK_BACKEND_WRITE: srv->network_backend_write = network_write_chunkqueue_write; break; #ifdef USE_WRITEV case NETWORK_BACKEND_WRITEV: srv->network_backend_write = network_write_chunkqueue_writev; break; #endif #ifdef USE_LINUX_SENDFILE case NETWORK_BACKEND_LINUX_SENDFILE: srv->network_backend_write = network_write_chunkqueue_linuxsendfile; break; #endif #ifdef USE_FREEBSD_SENDFILE case NETWORK_BACKEND_FREEBSD_SENDFILE: srv->network_backend_write = network_write_chunkqueue_freebsdsendfile; break; #endif #ifdef USE_SOLARIS_SENDFILEV case NETWORK_BACKEND_SOLARIS_SENDFILEV: srv->network_backend_write = network_write_chunkqueue_solarissendfilev; break; #endif default: return -1; } // 创建基于IP/端口的虚拟主机Web站点监听套接口描述符。 /* check for $SERVER["socket"] */ /* 配置正确格式如下: $SERVER["socket"] == "127.0.0.1:3001" {...} 0下标元素保存到的是基本全局配置信息,因此从索引1开始。 */ for (i = 1; i < srv->config_context->used; i++) { data_config *dc = (data_config *)srv->config_context->data[i]; specific_config *s = srv->config_storage[i]; size_t j; /* not our stage */ //不是虚拟主机配置项 if (COMP_SERVER_SOCKET != dc->comp) continue; if (dc->cond != CONFIG_COND_EQ) { //对于虚拟主机配置项只运行相等比较 log_error_write(srv, __FILE__, __LINE__, "s", "only == is allowed for $SERVER[\"socket\"]."); return -1; } /* check if we already know this socket, * if yes, don't init it */ /* srv->srv_socket为server_socket_array结构体类型,相关结构体都定义在头文件base.h内; 我们在下一个network_server_init()函数的分析中可以看到,所有已经被创建了监听套接口描述符的对应server_socket信息都会被记录在该字段内, 因此此处通过和该字段内保存的记录做比较可以知道该server_socket信息对应的监听套接口是否已经被创建 */ for (j = 0; j < srv->srv_sockets.used; j++) { if (buffer_is_equal(srv->srv_sockets.ptr[j]->srv_token, dc->string)) { break; } } if (j == srv->srv_sockets.used) { //比较完所有项都未匹配则表示还没创建 if (0 != network_server_init(srv, dc->string, s)) return -1; } } return 0; }
int config_set_defaults(server *srv) { size_t i; specific_config *s = srv->config_storage[0]; struct stat st1, st2; struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] = { /* - poll is most reliable * - select works everywhere * - linux-* are experimental */ #ifdef USE_POLL { FDEVENT_HANDLER_POLL, "poll" }, #endif #ifdef USE_SELECT { FDEVENT_HANDLER_SELECT, "select" }, #endif #ifdef USE_LINUX_EPOLL { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" }, #endif #ifdef USE_LINUX_SIGIO { FDEVENT_HANDLER_LINUX_RTSIG, "linux-rtsig" }, #endif #ifdef USE_SOLARIS_DEVPOLL { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" }, #endif #ifdef USE_FREEBSD_KQUEUE { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" }, { FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue" }, #endif { FDEVENT_HANDLER_UNSET, NULL } }; if (buffer_is_empty(s->document_root)) { log_error_write(srv, __FILE__, __LINE__, "s", "a default document-root has to be set"); return -1; } if (buffer_is_empty(srv->srvconf.changeroot)) { if (-1 == stat(s->document_root->ptr, &st1)) { log_error_write(srv, __FILE__, __LINE__, "sb", "base-docroot doesn't exist:", s->document_root); return -1; } } else { buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.changeroot); buffer_append_string_buffer(srv->tmp_buf, s->document_root); if (-1 == stat(srv->tmp_buf->ptr, &st1)) { log_error_write(srv, __FILE__, __LINE__, "sb", "base-docroot doesn't exist:", srv->tmp_buf); return -1; } } buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_lower(srv->tmp_buf); if (0 == stat(srv->tmp_buf->ptr, &st1)) { int is_lower = 0; is_lower = buffer_is_equal(srv->tmp_buf, s->document_root); /* lower-case existed, check upper-case */ buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_upper(srv->tmp_buf); /* we have to handle the special case that upper and lower-casing results in the same filename * as in server.document-root = "/" or "/12345/" */ if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) { /* lower-casing and upper-casing didn't result in * an other filename, no need to stat(), * just assume it is case-sensitive. */ s->force_lowercase_filenames = 0; } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { /* upper case exists too, doesn't the FS handle this ? */ /* upper and lower have the same inode -> case-insensitve FS */ if (st1.st_ino == st2.st_ino) { /* upper and lower have the same inode -> case-insensitve FS */ s->force_lowercase_filenames = 1; } } } if (srv->srvconf.port == 0) { srv->srvconf.port = s->is_ssl ? 443 : 80; } if (srv->srvconf.event_handler->used == 0) { /* choose a good default * * the event_handler list is sorted by 'goodness' * taking the first available should be the best solution */ srv->event_handler = event_handlers[0].et; if (FDEVENT_HANDLER_UNSET == srv->event_handler) { log_error_write(srv, __FILE__, __LINE__, "s", "sorry, there is no event handler for this system"); return -1; } } else { /* * User override */ for (i = 0; event_handlers[i].name; i++) { if (0 == strcmp(event_handlers[i].name, srv->srvconf.event_handler->ptr)) { srv->event_handler = event_handlers[i].et; break; } } if (FDEVENT_HANDLER_UNSET == srv->event_handler) { log_error_write(srv, __FILE__, __LINE__, "sb", "the selected event-handler in unknown or not supported:", srv->srvconf.event_handler ); return -1; } } if (s->is_ssl) { if (buffer_is_empty(s->ssl_pemfile)) { /* PEM file is require */ log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); return -1; } #ifndef USE_OPENSSL log_error_write(srv, __FILE__, __LINE__, "s", "ssl support is missing, recompile with --with-openssl"); return -1; #endif } return 0; }
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; }
lua_State *script_cache_get_script(server *srv, connection *con, script_cache *cache, buffer *name) { size_t i; script *sc = NULL; stat_cache_entry *sce; for (i = 0; i < cache->used; i++) { sc = cache->ptr[i]; if (buffer_is_equal(name, sc->name)) { sc->last_used = time(NULL); /* oops, the script failed last time */ if (lua_gettop(sc->L) == 0) break; force_assert(lua_gettop(sc->L) == 1); if (HANDLER_ERROR == stat_cache_get_entry(srv, con, sc->name, &sce)) { lua_pop(sc->L, 1); /* pop the old function */ break; } if (!buffer_is_equal(sce->etag, sc->etag)) { /* the etag is outdated, reload the function */ lua_pop(sc->L, 1); break; } force_assert(lua_isfunction(sc->L, -1)); return sc->L; } sc = NULL; } /* if the script was script already loaded but either got changed or * failed to load last time */ if (sc == NULL) { sc = script_init(); if (cache->size == 0) { cache->size = 16; cache->ptr = malloc(cache->size * sizeof(*(cache->ptr))); } else if (cache->used == cache->size) { cache->size += 16; cache->ptr = realloc(cache->ptr, cache->size * sizeof(*(cache->ptr))); } cache->ptr[cache->used++] = sc; buffer_copy_buffer(sc->name, name); sc->L = luaL_newstate(); luaL_openlibs(sc->L); } sc->last_used = time(NULL); if (0 != luaL_loadfile(sc->L, name->ptr)) { /* oops, an error, return it */ return sc->L; } if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, sc->name, &sce)) { buffer_copy_buffer(sc->etag, sce->etag); } force_assert(lua_isfunction(sc->L, -1)); return sc->L; }
int config_set_defaults(server *srv) { specific_config *s = &srv->config_storage[0]; #if 0 size_t i; struct stat st1, st2; #endif if (buffer_is_empty(s->document_root)) { log_error_write(srv, __FILE__, __LINE__, "s", "a default document-root has to be set"); return -1; } #if 0 buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_lower(srv->tmp_buf); if (0 == stat(srv->tmp_buf->ptr, &st1)) { int is_lower = 0; is_lower = buffer_is_equal(srv->tmp_buf, s->document_root); /* lower-case existed, check upper-case */ buffer_copy_string_buffer(srv->tmp_buf, s->document_root); buffer_to_upper(srv->tmp_buf); /* we have to handle the special case that upper and lower-casing results in the same filename * as in server.document-root = "/" or "/12345/" */ if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) { /* lower-casing and upper-casing didn't result in * an other filename, no need to stat(), * just assume it is case-sensitive. */ s->force_lowercase_filenames = 0; } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { /* upper case exists too, doesn't the FS handle this ? */ /* upper and lower have the same inode -> case-insensitve FS */ if (st1.st_ino == st2.st_ino) { /* upper and lower have the same inode -> case-insensitve FS */ s->force_lowercase_filenames = 1; } } } #endif if (srv->srvconf.port == 0) { srv->srvconf.port = s->is_ssl ? 322 : 554; } if (srv->srvconf.max_conns == 0) srv->srvconf.max_conns = 100; if (srv->srvconf.first_udp_port == 0) srv->srvconf.first_udp_port = RTP_DEFAULT_PORT; if (srv->srvconf.buffered_frames == 0) srv->srvconf.buffered_frames = BUFFERED_FRAMES_DEFAULT; if (s->is_ssl) { if (buffer_is_empty(s->ssl_pemfile)) { /* PEM file is require */ log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); return -1; } #ifndef USE_OPENSSL log_error_write(srv, __FILE__, __LINE__, "s", "ssl support is missing, recompile with --with-openssl"); return -1; #endif } 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_string_is_empty(con->uri.authority)) { /* * append server-port to the HTTP_POST if necessary */ l = con->uri.authority; switch(dc->cond) { case CONFIG_COND_NE: case CONFIG_COND_EQ: ck_colon = strchr(dc->string->ptr, ':'); val_colon = strchr(l->ptr, ':'); if (NULL != ck_colon && NULL == val_colon) { /* condition "host:port" but client send "host" */ buffer_copy_buffer(srv->cond_check_buf, l); buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":")); buffer_append_int(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr))); l = srv->cond_check_buf; } else if (NULL != val_colon && NULL == ck_colon) { /* condition "host" but client send "host:port" */ buffer_copy_string_len(srv->cond_check_buf, l->ptr, val_colon - l->ptr); l = srv->cond_check_buf; } break; default: break; } #if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT } else if (!buffer_string_is_empty(con->tlsext_server_name)) { l = con->tlsext_server_name; #endif } else { l = srv->empty_string; } break; } case COMP_HTTP_REMOTE_IP: { char *nm_slash; /* handle remoteip limitations * * "10.0.0.1" is provided for all comparisions * * only for == and != we support * * "10.0.0.1/24" */ if ((dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE) && (con->dst_addr.plain.sa_family == AF_INET) && (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) { int nm_bits; long nm; char *err; struct in_addr val_inp; if (*(nm_slash+1) == '\0') { log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string); return COND_RESULT_FALSE; } nm_bits = strtol(nm_slash + 1, &err, 10); if (*err) { log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, err); return COND_RESULT_FALSE; } if (nm_bits > 32 || nm_bits < 0) { log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: invalid netmask:", dc->string, err); return COND_RESULT_FALSE; } /* take IP convert to the native */ buffer_copy_string_len(srv->cond_check_buf, dc->string->ptr, nm_slash - dc->string->ptr); #ifdef __WIN32 if (INADDR_NONE == (val_inp.s_addr = inet_addr(srv->cond_check_buf->ptr))) { log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); return COND_RESULT_FALSE; } #else if (0 == inet_aton(srv->cond_check_buf->ptr, &val_inp)) { log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); return COND_RESULT_FALSE; } #endif /* build netmask */ nm = nm_bits ? htonl(~((1 << (32 - nm_bits)) - 1)) : 0; if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) { return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } } else { l = con->dst_addr_buf; } break; } case COMP_HTTP_SCHEME: l = con->uri.scheme; break; case COMP_HTTP_URL: l = con->uri.path; break; case COMP_HTTP_QUERY_STRING: l = con->uri.query; break; case COMP_SERVER_SOCKET: l = srv_sock->srv_token; break; case COMP_HTTP_REFERER: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Referer"))) { l = ds->value; } else { l = srv->empty_string; } break; } case COMP_HTTP_COOKIE: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Cookie"))) { l = ds->value; } else { l = srv->empty_string; } break; } case COMP_HTTP_USER_AGENT: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "User-Agent"))) { l = ds->value; } else { l = srv->empty_string; } break; } case COMP_HTTP_REQUEST_METHOD: { const char *method = get_http_method_name(con->request.http_method); /* we only have the request method as const char but we need a buffer for comparing */ buffer_copy_string(srv->tmp_buf, method); l = srv->tmp_buf; break; } case COMP_HTTP_LANGUAGE: { data_string *ds; if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "Accept-Language"))) { l = ds->value; } else { l = srv->empty_string; } break; } default: return COND_RESULT_FALSE; } if (NULL == l) { if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "bsbs", dc->comp_key, "(", l, ") compare to NULL"); } return COND_RESULT_FALSE; } if (con->conf.log_condition_handling) { log_error_write(srv, __FILE__, __LINE__, "bsbsb", dc->comp_key, "(", l, ") compare to ", dc->string); } switch(dc->cond) { case CONFIG_COND_NE: case CONFIG_COND_EQ: if (buffer_is_equal(l, dc->string)) { return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } break; #ifdef HAVE_PCRE_H case CONFIG_COND_NOMATCH: case CONFIG_COND_MATCH: { cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; int n; #ifndef elementsof #define elementsof(x) (sizeof(x) / sizeof(x[0])) #endif n = pcre_exec(dc->regex, dc->regex_study, CONST_BUF_LEN(l), 0, 0, cache->matches, elementsof(cache->matches)); cache->patterncount = n; if (n > 0) { cache->comp_value = l; cache->comp_type = dc->comp; return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE; } else { /* cache is already cleared */ return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE; } break; } #endif default: /* no way */ break; } return COND_RESULT_FALSE; }
static gboolean buffer_hash_equal(gconstpointer _a, gconstpointer _b) { buffer *a = (buffer *)_a; buffer *b = (buffer *)_b; return buffer_is_equal(a, b); }