static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; buffer *b = buffer_init(); double avg; time_t ts; char buf[32]; unsigned int k; unsigned int l; /* output total number of requests */ buffer_append_string_len(b, CONST_STR_LEN("Total Accesses: ")); avg = p->abs_requests; snprintf(buf, sizeof(buf) - 1, "%.0f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN("\n")); /* output total traffic out in kbytes */ buffer_append_string_len(b, CONST_STR_LEN("Total kBytes: ")); avg = p->abs_traffic_out / 1024; snprintf(buf, sizeof(buf) - 1, "%.0f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN("\n")); /* output uptime */ buffer_append_string_len(b, CONST_STR_LEN("Uptime: ")); ts = srv->cur_ts - srv->startup_ts; buffer_append_int(b, ts); buffer_append_string_len(b, CONST_STR_LEN("\n")); /* output busy servers */ buffer_append_string_len(b, CONST_STR_LEN("BusyServers: ")); buffer_append_int(b, srv->conns->used); buffer_append_string_len(b, CONST_STR_LEN("\n")); buffer_append_string_len(b, CONST_STR_LEN("IdleServers: ")); buffer_append_int(b, srv->conns->size - srv->conns->used); buffer_append_string_len(b, CONST_STR_LEN("\n")); /* output scoreboard */ buffer_append_string_len(b, CONST_STR_LEN("Scoreboard: ")); for (k = 0; k < srv->conns->used; k++) { connection *c = srv->conns->ptr[k]; const char *state = connection_get_short_state(c->state); buffer_append_string_len(b, state, 1); } for (l = 0; l < srv->conns->size - srv->conns->used; l++) { buffer_append_string_len(b, CONST_STR_LEN("_")); } buffer_append_string_len(b, CONST_STR_LEN("\n")); chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); /* set text/plain output */ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); return 0; }
static int mod_evhost_parse_host(connection *con,array *host) { register char *ptr = con->uri.authority->ptr + buffer_string_length(con->uri.authority); char *colon = ptr; /* needed to filter out the colon (if exists) */ int first = 1; data_string *ds; int i; /* first, find the domain + tld */ for(; ptr > con->uri.authority->ptr; ptr--) { if(*ptr == '.') { if(first) first = 0; else break; } else if(*ptr == ':') { colon = ptr; first = 1; } } ds = data_string_init(); buffer_copy_string_len(ds->key,CONST_STR_LEN("%0")); /* if we stopped at a dot, skip the dot */ if (*ptr == '.') ptr++; buffer_copy_string_len(ds->value, ptr, colon-ptr); array_insert_unique(host,(data_unset *)ds); /* if the : is not the start of the authority, go on parsing the hostname */ if (colon != con->uri.authority->ptr) { for(ptr = colon - 1, i = 1; ptr > con->uri.authority->ptr; ptr--) { if(*ptr == '.') { if (ptr != colon - 1) { /* is something between the dots */ ds = data_string_init(); buffer_copy_string_len(ds->key,CONST_STR_LEN("%")); buffer_append_int(ds->key, i++); buffer_copy_string_len(ds->value,ptr+1,colon-ptr-1); array_insert_unique(host,(data_unset *)ds); } colon = ptr; } } /* if the . is not the first charactor of the hostname */ if (colon != ptr) { ds = data_string_init(); buffer_copy_string_len(ds->key,CONST_STR_LEN("%")); buffer_append_int(ds->key, i /* ++ */); buffer_copy_string_len(ds->value,ptr,colon-ptr); array_insert_unique(host,(data_unset *)ds); } } return 0; }
static int log_buffer_prepare(buffer *b, server *srv, const char *filename, unsigned int line) { switch(srv->errorlog_mode) { case ERRORLOG_PIPE: case ERRORLOG_FILE: case ERRORLOG_FD: if (-1 == srv->errorlog_fd) return -1; /* cache the generated timestamp */ if (srv->cur_ts != srv->last_generated_debug_ts) { buffer_string_prepare_copy(srv->ts_debug_str, 255); buffer_append_strftime(srv->ts_debug_str, "%Y-%m-%d %H:%M:%S", localtime(&(srv->cur_ts))); srv->last_generated_debug_ts = srv->cur_ts; } buffer_copy_buffer(b, srv->ts_debug_str); buffer_append_string_len(b, CONST_STR_LEN(": (")); break; case ERRORLOG_SYSLOG: /* syslog is generating its own timestamps */ buffer_copy_string_len(b, CONST_STR_LEN("(")); break; } buffer_append_string(b, filename); buffer_append_string_len(b, CONST_STR_LEN(".")); buffer_append_int(b, line); buffer_append_string_len(b, CONST_STR_LEN(") ")); return 0; }
static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) { buffer *b; size_t i; array *st = srv->status; UNUSED(p_d); if (0 == st->used) { /* we have nothing to send */ con->http_status = 204; con->file_finished = 1; return HANDLER_FINISHED; } b = buffer_init(); for (i = 0; i < st->used; i++) { size_t ndx = st->sorted[i]; buffer_append_string_buffer(b, st->data[ndx]->key); buffer_append_string_len(b, CONST_STR_LEN(": ")); buffer_append_int(b, ((data_integer *)(st->data[ndx]))->value); buffer_append_string_len(b, CONST_STR_LEN("\n")); } chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); con->http_status = 200; con->file_finished = 1; return HANDLER_FINISHED; }
handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) { size_t i; stat_cache *sc = srv->stat_cache; size_t events; UNUSED(_fce); /* */ if (revent & FDEVENT_IN) { events = FAMPending(&sc->fam); for (i = 0; i < events; i++) { FAMEvent fe; fam_dir_entry *fam_dir; splay_tree *node; int ndx, j; FAMNextEvent(&sc->fam, &fe); /* handle event */ switch(fe.code) { case FAMChanged: case FAMDeleted: case FAMMoved: /* if the filename is a directory remove the entry */ fam_dir = fe.userdata; fam_dir->version++; /* file/dir is still here */ if (fe.code == FAMChanged) break; /* we have 2 versions, follow and no-follow-symlink */ for (j = 0; j < 2; j++) { buffer_copy_string(sc->hash_key, fe.filename); buffer_append_int(sc->hash_key, j); ndx = hashme(sc->hash_key); sc->dirs = splaytree_splay(sc->dirs, ndx); node = sc->dirs; if (node && (node->key == ndx)) { int osize = splaytree_size(sc->dirs); fam_dir_entry_free(&sc->fam, node->data); sc->dirs = splaytree_delete(sc->dirs, ndx); force_assert(osize - 1 == splaytree_size(sc->dirs)); } } break; default: break; } } } if (revent & FDEVENT_HUP) { /* fam closed the connection */ fdevent_event_del(srv->ev, &(sc->fam_fcce_ndx), FAMCONNECTION_GETFD(&sc->fam)); fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&sc->fam)); FAMClose(&sc->fam); } return HANDLER_GO_ON; }
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; }
/* lowercase: append space, uppercase: don't */ static void log_buffer_append_printf(buffer *out, const char *fmt, va_list ap) { for(; *fmt; fmt++) { int d; char *s; buffer *b; off_t o; switch(*fmt) { case 's': /* string */ s = va_arg(ap, char *); buffer_append_string_c_escaped(out, s, (NULL != s) ? strlen(s) : 0); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'b': /* buffer */ b = va_arg(ap, buffer *); buffer_append_string_c_escaped(out, CONST_BUF_LEN(b)); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'd': /* int */ d = va_arg(ap, int); buffer_append_int(out, d); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'o': /* off_t */ o = va_arg(ap, off_t); buffer_append_int(out, o); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'x': /* int (hex) */ d = va_arg(ap, int); buffer_append_string_len(out, CONST_STR_LEN("0x")); buffer_append_uint_hex(out, d); buffer_append_string_len(out, CONST_STR_LEN(" ")); break; case 'S': /* string */ s = va_arg(ap, char *); buffer_append_string_c_escaped(out, s, (NULL != s) ? strlen(s) : 0); break; case 'B': /* buffer */ b = va_arg(ap, buffer *); buffer_append_string_c_escaped(out, CONST_BUF_LEN(b)); break; case 'D': /* int */ d = va_arg(ap, int); buffer_append_int(out, d); break; case 'O': /* off_t */ o = va_arg(ap, off_t); buffer_append_int(out, o); break; case 'X': /* int (hex) */ d = va_arg(ap, int); buffer_append_string_len(out, CONST_STR_LEN("0x")); buffer_append_uint_hex(out, d); break; case '(': case ')': case '<': case '>': case ',': case ' ': buffer_append_string_len(out, fmt, 1); break; } } }
static int connection_handle_write_prepare(server *srv, connection *con) { if (con->mode == DIRECT) { /* static files */ switch(con->request.http_method) { case HTTP_METHOD_GET: case HTTP_METHOD_POST: case HTTP_METHOD_HEAD: break; case HTTP_METHOD_OPTIONS: /* * 400 is coming from the request-parser BEFORE uri.path is set * 403 is from the response handler when noone else catched it * * */ if ((!con->http_status || con->http_status == 200) && !buffer_string_is_empty(con->uri.path) && con->uri.path->ptr[0] != '*') { response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; con->http_status = 200; con->file_finished = 1; chunkqueue_reset(con->write_queue); } break; default: if (0 == con->http_status) { con->http_status = 501; } break; } } if (con->http_status == 0) { con->http_status = 403; } switch(con->http_status) { case 204: /* class: header only */ case 205: case 304: /* disable chunked encoding again as we have no body */ con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; con->parsed_response &= ~HTTP_CONTENT_LENGTH; chunkqueue_reset(con->write_queue); con->file_finished = 1; break; default: /* class: header + body */ if (con->mode != DIRECT) break; /* only custom body for 4xx and 5xx */ if (con->http_status < 400 || con->http_status >= 600) break; con->file_finished = 0; connection_handle_errdoc_init(srv, con); /* try to send static errorfile */ if (!buffer_string_is_empty(con->conf.errorfile_prefix)) { stat_cache_entry *sce = NULL; buffer_copy_buffer(con->physical.path, con->conf.errorfile_prefix); buffer_append_int(con->physical.path, con->http_status); buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); if (0 == http_chunk_append_file(srv, con, con->physical.path)) { con->file_finished = 1; if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); } } } if (!con->file_finished) { buffer *b; buffer_reset(con->physical.path); con->file_finished = 1; b = buffer_init(); /* build default error-page */ buffer_copy_string_len(b, CONST_STR_LEN( "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" " <head>\n" " <title>")); buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN( "</title>\n" " </head>\n" " <body>\n" " <h1>")); buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" - ")); buffer_append_string(b, get_http_status_name(con->http_status)); buffer_append_string_len(b, CONST_STR_LEN("</h1>\n" " </body>\n" "</html>\n" )); (void)http_chunk_append_buffer(srv, con, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); } break; } /* Allow filter plugins to change response headers before they are written. */ switch(plugins_call_handle_response_start(srv, con)) { case HANDLER_GO_ON: case HANDLER_FINISHED: break; default: log_error_write(srv, __FILE__, __LINE__, "s", "response_start plugin failed"); return -1; } if (con->file_finished) { /* we have all the content and chunked encoding is not used, set a content-length */ if ((!(con->parsed_response & HTTP_CONTENT_LENGTH)) && (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0) { off_t qlen = chunkqueue_length(con->write_queue); /** * The Content-Length header only can be sent if we have content: * - HEAD doesn't have a content-body (but have a content-length) * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3) * * Otherwise generate a Content-Length header as chunked encoding is not * available */ if ((con->http_status >= 100 && con->http_status < 200) || con->http_status == 204 || con->http_status == 304) { data_string *ds; /* no Content-Body, no Content-Length */ if (NULL != (ds = (data_string*) array_get_element(con->response.headers, "Content-Length"))) { buffer_reset(ds->value); /* Headers with empty values are ignored for output */ } } else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) { /* qlen = 0 is important for Redirects (301, ...) as they MAY have * a content. Browsers are waiting for a Content otherwise */ buffer_copy_int(srv->tmp_buf, qlen); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); } } } else { /** * the file isn't finished yet, but we have all headers * * to get keep-alive we either need: * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or * - Transfer-Encoding: chunked (HTTP/1.1) */ if (((con->parsed_response & HTTP_CONTENT_LENGTH) == 0) && ((con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) == 0)) { if (con->request.http_version == HTTP_VERSION_1_1) { off_t qlen = chunkqueue_length(con->write_queue); con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; if (qlen) { /* create initial Transfer-Encoding: chunked segment */ buffer *b = srv->tmp_chunk_len; buffer_string_set_length(b, 0); buffer_append_uint_hex(b, (uintmax_t)qlen); buffer_append_string_len(b, CONST_STR_LEN("\r\n")); chunkqueue_prepend_buffer(con->write_queue, b); chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("\r\n")); } } else { con->keep_alive = 0; } } /** * if the backend sent a Connection: close, follow the wish * * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we * will close the connection. That's fine. We can always decide the close * the connection * * FIXME: to be nice we should remove the Connection: ... */ if (con->parsed_response & HTTP_CONNECTION) { /* a subrequest disable keep-alive although the client wanted it */ if (con->keep_alive && !con->response.keep_alive) { con->keep_alive = 0; } } } if (con->request.http_method == HTTP_METHOD_HEAD) { /** * a HEAD request has the same as a GET * without the content */ con->file_finished = 1; chunkqueue_reset(con->write_queue); con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; } http_response_write_header(srv, con); return 0; }
int connection_state_machine(server *srv, connection *con) { int done = 0, r; if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at start", con->fd, connection_get_state(con->state)); } while (done == 0) { size_t ostate = con->state; if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state for fd", con->fd, connection_get_state(con->state)); } switch (con->state) { case CON_STATE_REQUEST_START: /* transient */ con->request_start = srv->cur_ts; con->read_idle_ts = srv->cur_ts; if (con->conf.high_precision_timestamps) log_clock_gettime_realtime(&con->request_start_hp); con->request_count++; con->loops_per_request = 0; connection_set_state(srv, con, CON_STATE_READ); break; case CON_STATE_REQUEST_END: /* transient */ buffer_reset(con->uri.authority); buffer_reset(con->uri.path); buffer_reset(con->uri.query); buffer_reset(con->request.orig_uri); if (http_request_parse(srv, con)) { /* we have to read some data from the POST request */ connection_set_state(srv, con, CON_STATE_READ_POST); break; } connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); break; case CON_STATE_READ_POST: case CON_STATE_HANDLE_REQUEST: /* * the request is parsed * * decided what to do with the request * - * * */ switch (r = http_response_prepare(srv, con)) { case HANDLER_WAIT_FOR_EVENT: if (!con->file_finished && (!con->file_started || 0 == con->conf.stream_response_body)) { break; /* come back here */ } /* response headers received from backend; fall through to start response */ case HANDLER_FINISHED: if (con->error_handler_saved_status > 0) { con->request.http_method = con->error_handler_saved_method; } if (con->mode == DIRECT) { if (con->error_handler_saved_status) { if (con->error_handler_saved_status > 0) { con->http_status = con->error_handler_saved_status; } else if (con->http_status == 404 || con->http_status == 403) { /* error-handler-404 is a 404 */ con->http_status = -con->error_handler_saved_status; } else { /* error-handler-404 is back and has generated content */ /* if Status: was set, take it otherwise use 200 */ } } else if (con->http_status >= 400) { buffer *error_handler = NULL; if (!buffer_string_is_empty(con->conf.error_handler)) { error_handler = con->conf.error_handler; } else if ((con->http_status == 404 || con->http_status == 403) && !buffer_string_is_empty(con->conf.error_handler_404)) { error_handler = con->conf.error_handler_404; } if (error_handler) { /* call error-handler */ /* set REDIRECT_STATUS to save current HTTP status code * for access by dynamic handlers * https://redmine.lighttpd.net/issues/1828 */ data_string *ds; if (NULL == (ds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { ds = data_string_init(); } buffer_copy_string_len(ds->key, CONST_STR_LEN("REDIRECT_STATUS")); buffer_append_int(ds->value, con->http_status); array_insert_unique(con->environment, (data_unset *)ds); if (error_handler == con->conf.error_handler) { plugins_call_connection_reset(srv, con); if (con->request.content_length) { if (con->request.content_length != con->request_content_queue->bytes_in) { con->keep_alive = 0; } con->request.content_length = 0; chunkqueue_reset(con->request_content_queue); } con->is_writable = 1; con->file_finished = 0; con->file_started = 0; con->got_response = 0; con->parsed_response = 0; con->response.keep_alive = 0; con->response.content_length = -1; con->response.transfer_encoding = 0; con->error_handler_saved_status = con->http_status; con->error_handler_saved_method = con->request.http_method; con->request.http_method = HTTP_METHOD_GET; } else { /*(preserve behavior for server.error-handler-404)*/ con->error_handler_saved_status = -con->http_status; /*(negative to flag old behavior)*/ } buffer_copy_buffer(con->request.uri, error_handler); connection_handle_errdoc_init(srv, con); con->http_status = 0; /*(after connection_handle_errdoc_init())*/ done = -1; break; } } } if (con->http_status == 0) con->http_status = 200; /* we have something to send, go on */ connection_set_state(srv, con, CON_STATE_RESPONSE_START); break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); break; case HANDLER_COMEBACK: done = -1; break; case HANDLER_ERROR: /* something went wrong */ connection_set_state(srv, con, CON_STATE_ERROR); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); break; } if (con->state == CON_STATE_HANDLE_REQUEST && ostate == CON_STATE_READ_POST) { ostate = CON_STATE_HANDLE_REQUEST; } break; case CON_STATE_RESPONSE_START: /* * the decision is done * - create the HTTP-Response-Header * */ if (-1 == connection_handle_write_prepare(srv, con)) { connection_set_state(srv, con, CON_STATE_ERROR); break; } connection_set_state(srv, con, CON_STATE_WRITE); break; case CON_STATE_RESPONSE_END: /* transient */ case CON_STATE_ERROR: /* transient */ connection_handle_response_end_state(srv, con); break; case CON_STATE_CONNECT: chunkqueue_reset(con->read_queue); con->request_count = 0; break; case CON_STATE_CLOSE: connection_handle_close_state(srv, con); break; case CON_STATE_READ: connection_handle_read_state(srv, con); break; case CON_STATE_WRITE: do { /* only try to write if we have something in the queue */ if (!chunkqueue_is_empty(con->write_queue)) { if (con->is_writable) { if (-1 == connection_handle_write(srv, con)) { log_error_write(srv, __FILE__, __LINE__, "ds", con->fd, "handle write failed."); connection_set_state(srv, con, CON_STATE_ERROR); break; } if (con->state != CON_STATE_WRITE) break; } } else if (con->file_finished) { connection_set_state(srv, con, CON_STATE_RESPONSE_END); break; } if (con->mode != DIRECT && !con->file_finished) { switch(r = plugins_call_handle_subrequest(srv, con)) { case HANDLER_WAIT_FOR_EVENT: case HANDLER_FINISHED: case HANDLER_GO_ON: break; case HANDLER_WAIT_FOR_FD: srv->want_fds++; fdwaitqueue_append(srv, con); break; case HANDLER_COMEBACK: default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unexpected subrequest handler ret-value: ", con->fd, r); /* fall through */ case HANDLER_ERROR: connection_set_state(srv, con, CON_STATE_ERROR); break; } } } while (con->state == CON_STATE_WRITE && (!chunkqueue_is_empty(con->write_queue) ? con->is_writable : con->file_finished)); break; default: log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown state:", con->fd, con->state); break; } if (done == -1) { done = 0; } else if (ostate == con->state) { done = 1; } } if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "state at exit:", con->fd, connection_get_state(con->state)); } r = 0; switch(con->state) { case CON_STATE_READ: case CON_STATE_CLOSE: r = FDEVENT_IN; break; case CON_STATE_WRITE: /* request write-fdevent only if we really need it * - if we have data to write * - if the socket is not writable yet */ if (!chunkqueue_is_empty(con->write_queue) && (con->is_writable == 0) && (con->traffic_limit_reached == 0)) { r |= FDEVENT_OUT; } /* fall through */ case CON_STATE_READ_POST: if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN) { r |= FDEVENT_IN; } break; default: break; } if (-1 != con->fd) { const int events = fdevent_event_get_interest(srv->ev, con->fd); if (con->is_readable < 0) { con->is_readable = 0; r |= FDEVENT_IN; } if (con->is_writable < 0) { con->is_writable = 0; r |= FDEVENT_OUT; } if (r != events) { /* update timestamps when enabling interest in events */ if ((r & FDEVENT_IN) && !(events & FDEVENT_IN)) { con->read_idle_ts = srv->cur_ts; } if ((r & FDEVENT_OUT) && !(events & FDEVENT_OUT)) { con->write_request_ts = srv->cur_ts; } fdevent_event_set(srv->ev, &con->fde_ndx, con->fd, r); } } return 0; }
static handler_t mod_status_handle_server_status_html(server *srv, connection *con, void *p_d) { plugin_data *p = p_d; buffer *b = buffer_init(); size_t j; double avg; char multiplier = '\0'; char buf[32]; time_t ts; int days, hours, mins, seconds; 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>Status</title>\n" " <style type=\"text/css\">\n" " table.status { border: black solid thin; }\n" " td { white-space: nowrap; }\n" " td.int { background-color: #f0f0f0; text-align: right }\n" " td.string { background-color: #f0f0f0; text-align: left }\n" " th.status { background-color: black; color: white; font-weight: bold; }\n" " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n" " span.sortarrow { color: white; text-decoration: none; }\n" " </style>\n")); if (p->conf.sort) { buffer_append_string_len(b, CONST_STR_LEN( "<script type=\"text/javascript\">\n" "// <!--\n" "var sort_column;\n" "var prev_span = null;\n" "function get_inner_text(el) {\n" " if((typeof el == 'string')||(typeof el == 'undefined'))\n" " return el;\n" " if(el.innerText)\n" " return el.innerText;\n" " else {\n" " var str = \"\";\n" " var cs = el.childNodes;\n" " var l = cs.length;\n" " for (i=0;i<l;i++) {\n" " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n" " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n" " }\n" " }\n" " return str;\n" "}\n" "function sortfn(a,b) {\n" " var at = get_inner_text(a.cells[sort_column]);\n" " var bt = get_inner_text(b.cells[sort_column]);\n" " if (a.cells[sort_column].className == 'int') {\n" " return parseInt(at)-parseInt(bt);\n" " } else {\n" " aa = at.toLowerCase();\n" " bb = bt.toLowerCase();\n" " if (aa==bb) return 0;\n" " else if (aa<bb) return -1;\n" " else return 1;\n" " }\n" "}\n" "function resort(lnk) {\n" " var span = lnk.childNodes[1];\n" " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n" " var rows = new Array();\n" " for (j=1;j<table.rows.length;j++)\n" " rows[j-1] = table.rows[j];\n" " sort_column = lnk.parentNode.cellIndex;\n" " rows.sort(sortfn);\n" " if (prev_span != null) prev_span.innerHTML = '';\n" " if (span.getAttribute('sortdir')=='down') {\n" " span.innerHTML = '↑';\n" " span.setAttribute('sortdir','up');\n" " rows.reverse();\n" " } else {\n" " span.innerHTML = '↓';\n" " span.setAttribute('sortdir','down');\n" " }\n" " for (i=0;i<rows.length;i++)\n" " table.tBodies[0].appendChild(rows[i]);\n" " prev_span = span;\n" "}\n" "// -->\n" "</script>\n")); } buffer_append_string_len(b, CONST_STR_LEN( " </head>\n" " <body>\n")); /* connection listing */ buffer_append_string_len(b, CONST_STR_LEN("<h1>Server-Status (" PACKAGE_NAME " " PACKAGE_VERSION ")</h1>")); buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">")); buffer_append_string_buffer(b, con->uri.authority); buffer_append_string_len(b, CONST_STR_LEN(" (")); buffer_append_string_buffer(b, con->server_name); buffer_append_string_len(b, CONST_STR_LEN(")</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">")); ts = srv->cur_ts - srv->startup_ts; days = ts / (60 * 60 * 24); ts %= (60 * 60 * 24); hours = ts / (60 * 60); ts %= (60 * 60); mins = ts / (60); ts %= (60); seconds = ts; if (days) { buffer_append_int(b, days); buffer_append_string_len(b, CONST_STR_LEN(" days ")); } if (hours) { buffer_append_int(b, hours); buffer_append_string_len(b, CONST_STR_LEN(" hours ")); } if (mins) { buffer_append_int(b, mins); buffer_append_string_len(b, CONST_STR_LEN(" min ")); } buffer_append_int(b, seconds); buffer_append_string_len(b, CONST_STR_LEN(" s")); buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">")); ts = srv->startup_ts; strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts)); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); avg = p->abs_requests; mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_int(b, avg); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("req</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); avg = p->abs_traffic_out; mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("byte</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); avg = p->abs_requests / (srv->cur_ts - srv->startup_ts); mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_int(b, avg); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts); mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n")); for (j = 0, avg = 0; j < 5; j++) { avg += p->mod_5s_requests[j]; } avg /= 5; buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); mod_status_get_multiplier(&avg, &multiplier, 1000); buffer_append_int(b, avg); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n")); for (j = 0, avg = 0; j < 5; j++) { avg += p->mod_5s_traffic_out[j]; } avg /= 5; buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); mod_status_get_multiplier(&avg, &multiplier, 1024); sprintf(buf, "%.2f", avg); buffer_append_string(b, buf); buffer_append_string_len(b, CONST_STR_LEN(" ")); if (multiplier) buffer_append_string_len(b, &multiplier, 1); buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n")); buffer_append_string_len(b, CONST_STR_LEN("</table>\n")); buffer_append_string_len(b, CONST_STR_LEN( "<hr />\n<pre><b>legend</b>\n" ". = connect, C = close, E = hard error, k = keep-alive\n" "r = read, R = read-POST, W = write, h = handle-request\n" "q = request-start, Q = request-end\n" "s = response-start, S = response-end\n")); buffer_append_string_len(b, CONST_STR_LEN("<b>")); buffer_append_int(b, srv->conns->used); buffer_append_string_len(b, CONST_STR_LEN(" connections</b>\n")); for (j = 0; j < srv->conns->used; j++) { connection *c = srv->conns->ptr[j]; const char *state; if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) { state = "k"; } else { state = connection_get_short_state(c->state); } buffer_append_string_len(b, state, 1); if (((j + 1) % 50) == 0) { buffer_append_string_len(b, CONST_STR_LEN("\n")); } } buffer_append_string_len(b, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n")); buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n")); buffer_append_string_len(b, CONST_STR_LEN("<tr>")); mod_status_header_append_sort(b, p_d, "Client IP"); mod_status_header_append_sort(b, p_d, "Read"); mod_status_header_append_sort(b, p_d, "Written"); mod_status_header_append_sort(b, p_d, "State"); mod_status_header_append_sort(b, p_d, "Time"); mod_status_header_append_sort(b, p_d, "Host"); mod_status_header_append_sort(b, p_d, "URI"); mod_status_header_append_sort(b, p_d, "File"); buffer_append_string_len(b, CONST_STR_LEN("</tr>\n")); for (j = 0; j < srv->conns->used; j++) { connection *c = srv->conns->ptr[j]; buffer_append_string_len(b, CONST_STR_LEN("<tr><td class=\"string\">")); buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr))); buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); if (c->request.content_length) { buffer_append_int(b, c->request_content_queue->bytes_in); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_int(b, c->request.content_length); } else { buffer_append_string_len(b, CONST_STR_LEN("0/0")); } buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); buffer_append_int(b, c->write_queue->bytes_out); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_int(b, c->write_queue->bytes_out + chunkqueue_length(c->write_queue)); buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) { buffer_append_string_len(b, CONST_STR_LEN("keep-alive")); } else { buffer_append_string(b, connection_get_state(c->state)); } buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); buffer_append_int(b, srv->cur_ts - c->request_start); buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); if (buffer_string_is_empty(c->server_name)) { buffer_append_string_buffer(b, c->uri.authority); } else { buffer_append_string_buffer(b, c->server_name); } buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); if (!buffer_string_is_empty(c->uri.path)) { buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML); } if (!buffer_string_is_empty(c->uri.query)) { buffer_append_string_len(b, CONST_STR_LEN("?")); buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.query), ENCODING_HTML); } if (!buffer_string_is_empty(c->request.orig_uri)) { buffer_append_string_len(b, CONST_STR_LEN(" (")); buffer_append_string_encoded(b, CONST_BUF_LEN(c->request.orig_uri), ENCODING_HTML); buffer_append_string_len(b, CONST_STR_LEN(")")); } buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); buffer_append_string_buffer(b, c->physical.path); buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); } buffer_append_string_len(b, CONST_STR_LEN( "</table>\n")); buffer_append_string_len(b, CONST_STR_LEN( " </body>\n" "</html>\n" )); chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); return 0; }
int http_response_write_header(server *srv, connection *con) { buffer *b; size_t i; int have_date = 0; int have_server = 0; b = buffer_init(); if (con->request.http_version == HTTP_VERSION_1_1) { buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 ")); } else { buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 ")); } buffer_append_int(b, con->http_status); buffer_append_string_len(b, CONST_STR_LEN(" ")); buffer_append_string(b, get_http_status_name(con->http_status)); /* disable keep-alive if requested */ if (con->request_count > con->conf.max_keep_alive_requests || 0 == con->conf.max_keep_alive_idle) { con->keep_alive = 0; } else { con->keep_alive_idle = con->conf.max_keep_alive_idle; } if (con->request.http_version != HTTP_VERSION_1_1 || con->keep_alive == 0) { if (con->keep_alive) { response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); } else { response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); } } if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { response_header_overwrite(srv, con, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked")); } /* add all headers */ for (i = 0; i < con->response.headers->used; i++) { data_string *ds; ds = (data_string *)con->response.headers->data[i]; if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key) && 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-")) && 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-Sendfile"))) { if (0 == strcasecmp(ds->key->ptr, "Date")) have_date = 1; if (0 == strcasecmp(ds->key->ptr, "Server")) have_server = 1; if (0 == strcasecmp(ds->key->ptr, "Content-Encoding") && 304 == con->http_status) continue; buffer_append_string_len(b, CONST_STR_LEN("\r\n")); buffer_append_string_buffer(b, ds->key); buffer_append_string_len(b, CONST_STR_LEN(": ")); #if 0 /** * the value might contain newlines, encode them with at least one white-space */ buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_HTTP_HEADER); #else buffer_append_string_buffer(b, ds->value); #endif } } if (!have_date) { /* HTTP/1.1 requires a Date: header */ buffer_append_string_len(b, CONST_STR_LEN("\r\nDate: ")); /* cache the generated timestamp */ if (srv->cur_ts != srv->last_generated_date_ts) { buffer_string_prepare_copy(srv->ts_date_str, 255); buffer_append_strftime(srv->ts_date_str, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts))); srv->last_generated_date_ts = srv->cur_ts; } buffer_append_string_buffer(b, srv->ts_date_str); } if (!have_server) { if (buffer_is_empty(con->conf.server_tag)) { buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: " PACKAGE_DESC)); } else if (!buffer_string_is_empty(con->conf.server_tag)) { buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: ")); buffer_append_string_encoded(b, CONST_BUF_LEN(con->conf.server_tag), ENCODING_HTTP_HEADER); } } buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); con->bytes_header = buffer_string_length(b); if (con->conf.log_response_header) { log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b); } chunkqueue_prepend_buffer(con->write_queue, b); buffer_free(b); return 0; }
void buffer_copy_int(buffer *b, intmax_t val) { force_assert(NULL != b); b->used = 0; buffer_append_int(b, val); }
int http_response_redirect_to_directory(server *srv, connection *con) { buffer *o; o = buffer_init(); buffer_copy_buffer(o, con->uri.scheme); buffer_append_string_len(o, CONST_STR_LEN("://")); if (!buffer_is_empty(con->uri.authority)) { buffer_append_string_buffer(o, con->uri.authority); } else { /* get the name of the currently connected socket */ struct hostent *he; #ifdef HAVE_IPV6 char hbuf[256]; #endif sock_addr our_addr; socklen_t our_addr_len; our_addr_len = sizeof(our_addr); if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) { con->http_status = 500; log_error_write(srv, __FILE__, __LINE__, "ss", "can't get sockname", strerror(errno)); buffer_free(o); return 0; } /* Lookup name: secondly try to get hostname for bind address */ switch(our_addr.plain.sa_family) { #ifdef HAVE_IPV6 case AF_INET6: if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6), SA_LEN((const struct sockaddr *)&our_addr.ipv6), hbuf, sizeof(hbuf), NULL, 0, 0)) { char dst[INET6_ADDRSTRLEN]; log_error_write(srv, __FILE__, __LINE__, "SSS", "NOTICE: getnameinfo failed: ", strerror(errno), ", using ip-address instead"); buffer_append_string(o, inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr, dst, sizeof(dst))); } else { buffer_append_string(o, hbuf); } break; #endif case AF_INET: if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) { log_error_write(srv, __FILE__, __LINE__, "SdS", "NOTICE: gethostbyaddr failed: ", h_errno, ", using ip-address instead"); buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr)); } else { buffer_append_string(o, he->h_name); } break; default: log_error_write(srv, __FILE__, __LINE__, "S", "ERROR: unsupported address-type"); buffer_free(o); return -1; } { unsigned short default_port = 80; if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) { default_port = 443; } if (default_port != srv->srvconf.port) { buffer_append_string_len(o, CONST_STR_LEN(":")); buffer_append_int(o, srv->srvconf.port); } } } buffer_append_string_buffer(o, con->uri.path); buffer_append_string_len(o, CONST_STR_LEN("/")); if (!buffer_string_is_empty(con->uri.query)) { buffer_append_string_len(o, CONST_STR_LEN("?")); buffer_append_string_buffer(o, con->uri.query); } response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o)); con->http_status = 301; con->file_finished = 1; buffer_free(o); return 0; }
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 = buffer_init(); 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_int(b, start); buffer_append_string_len(b, CONST_STR_LEN("-")); buffer_append_int(b, end); buffer_append_string_len(b, CONST_STR_LEN("/")); buffer_append_int(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 += buffer_string_length(b); chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); } 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 = buffer_init(); 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 += buffer_string_length(b); chunkqueue_append_buffer(con->write_queue, b); buffer_free(b); /* 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_int(p->range_buf, start); buffer_append_string_len(p->range_buf, CONST_STR_LEN("-")); buffer_append_int(p->range_buf, end); buffer_append_string_len(p->range_buf, CONST_STR_LEN("/")); buffer_append_int(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; }
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; }
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; }
int http_request_host_normalize(buffer *b) { /* * check for and canonicalize numeric IP address and portnum (optional) * (IP address may be followed by ":portnum" (optional)) * - IPv6: "[...]" * - IPv4: "x.x.x.x" * - IPv4: 12345678 (32-bit decimal number) * - IPv4: 012345678 (32-bit octal number) * - IPv4: 0x12345678 (32-bit hex number) * * allow any chars (except ':' and '\0' and stray '[' or ']') * (other code may check chars more strictly or more pedantically) * ':' delimits (optional) port at end of string * "[]" wraps IPv6 address literal * '\0' should have been rejected earlier were it present * * any chars includes, but is not limited to: * - allow '-' any where, even at beginning of word * (security caution: might be confused for cmd flag if passed to shell) * - allow all-digit TLDs * (might be mistaken for IPv4 addr by inet_aton() * unless non-digits appear in subdomain) */ /* Note: not using getaddrinfo() since it does not support "[]" around IPv6 * and is not as lenient as inet_aton() and inet_addr() for IPv4 strings. * Not using inet_pton() (when available) on IPv4 for similar reasons. */ const char * const p = b->ptr; const size_t blen = buffer_string_length(b); long port = 0; if (*p != '[') { char * const colon = (char *)memchr(p, ':', blen); if (colon) { if (*p == ':') return -1; /*(empty host then port, or naked IPv6)*/ if (colon[1] != '\0') { char *e; port = strtol(colon+1, &e, 0); /*(allow decimal, octal, hex)*/ if (0 < port && port <= USHRT_MAX && *e == '\0') { /* valid port */ } else { return -1; } } /*(else ignore stray colon at string end)*/ buffer_string_set_length(b, (size_t)(colon - p)); /*(remove port str)*/ } if (light_isdigit(*p)) { /* (IPv4 address literal or domain starting w/ digit (e.g. 3com))*/ struct in_addr addr; #if defined(HAVE_INET_ATON) /*(Windows does not provide inet_aton())*/ if (0 != inet_aton(p, &addr)) #else if ((addr.s_addr = inet_addr(p)) != INADDR_NONE) #endif { #if defined(HAVE_INET_PTON)/*(expect inet_ntop() if inet_pton())*/ #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 #endif char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, (const void *)&addr, buf, sizeof(buf)); buffer_copy_string(b, buf); #else buffer_copy_string(b, inet_ntoa(addr)); /*(not thread-safe)*/ #endif } } } else { /* IPv6 addr */ #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) struct in6_addr addr; char *bracket = b->ptr+blen-1; char *percent = strchr(b->ptr+1, '%'); size_t len; int rc; char buf[INET6_ADDRSTRLEN+16]; /*(+16 for potential %interface name)*/ if (blen <= 2) return -1; /*(invalid "[]")*/ if (*bracket != ']') { bracket = (char *)memchr(b->ptr+1, ']', blen-1); if (NULL == bracket || bracket[1] != ':' || bracket - b->ptr == 1){ return -1; } if (bracket[2] != '\0') { /*(ignore stray colon at string end)*/ char *e; port = strtol(bracket+2, &e, 0); /*(allow decimal, octal, hex)*/ if (0 < port && port <= USHRT_MAX && *e == '\0') { /* valid port */ } else { return -1; } } } *bracket = '\0';/*(terminate IPv6 string)*/ if (percent) *percent = '\0'; /*(remove %interface from address)*/ rc = inet_pton(AF_INET6, b->ptr+1, &addr); if (percent) *percent = '%'; /*(restore %interface)*/ *bracket = ']'; /*(restore bracket)*/ if (1 != rc) return -1; inet_ntop(AF_INET6,(const void *)&addr, buf, sizeof(buf)); len = strlen(buf); if (percent) { if (percent > bracket) return -1; if (len + (size_t)(bracket - percent) >= sizeof(buf)) return -1; memcpy(buf+len, percent, (size_t)(bracket - percent)); len += (size_t)(bracket - percent); } buffer_string_set_length(b, 1); /* truncate after '[' */ buffer_append_string_len(b, buf, len); buffer_append_string_len(b, CONST_STR_LEN("]")); #else return -1; #endif } if (port) { buffer_append_string_len(b, CONST_STR_LEN(":")); buffer_append_int(b, (int)port); } return 0; }