From c21c3b0befeb46a51b6bf3758ffa30813bea0ff0 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 9 Mar 2024 14:19:22 +0100 Subject: Adding upstream version 1.44.3. Signed-off-by: Daniel Baumann --- fluent-bit/lib/monkey/mk_server/mk_vhost.c | 821 +++++++++++++++++++++++++++++ 1 file changed, 821 insertions(+) create mode 100644 fluent-bit/lib/monkey/mk_server/mk_vhost.c (limited to 'fluent-bit/lib/monkey/mk_server/mk_vhost.c') diff --git a/fluent-bit/lib/monkey/mk_server/mk_vhost.c b/fluent-bit/lib/monkey/mk_server/mk_vhost.c new file mode 100644 index 000000000..2dc35fb1f --- /dev/null +++ b/fluent-bit/lib/monkey/mk_server/mk_vhost.c @@ -0,0 +1,821 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* Monkey HTTP Server + * ================== + * Copyright 2001-2017 Eduardo Silva + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +static int str_to_regex(char *str, regex_t *reg) +{ + char *p = str; + regex_t *result; + + while (*p) { + if (*p == ' ') *p = '|'; + p++; + } + + result = re_compile(str); + + memcpy(reg, result, REGEXP_SIZE); + + return 0; +} + +/* + * This function is triggered upon thread creation (inside the thread + * context), here we configure per-thread data. + */ +int mk_vhost_fdt_worker_init(struct mk_server *server) +{ + int i; + int j; + struct mk_vhost *h; + struct mk_list *list; + struct mk_list *head; + struct vhost_fdt_host *fdt; + struct vhost_fdt_hash_table *ht; + struct vhost_fdt_hash_chain *hc; + + if (server->fdt == MK_FALSE) { + return -1; + } + + /* + * We are under a thread context and the main configuration is + * already in place. Now for every existent virtual host we are + * going to create the File Descriptor Table (FDT) which aims to + * hold references of 'open and shared' file descriptors under + * the Virtual Host context. + */ + + /* + * Under an initialization context we need to protect this critical + * section + */ + pthread_mutex_lock(&server->vhost_fdt_mutex); + + /* + * Initialize the thread FDT/Hosts list and create an entry per + * existent virtual host + */ + list = mk_mem_alloc_z(sizeof(struct mk_list)); + mk_list_init(list); + + mk_list_foreach(head, &server->hosts) { + h = mk_list_entry(head, struct mk_vhost, _head); + + fdt = mk_mem_alloc(sizeof(struct vhost_fdt_host)); + fdt->host = h; + + /* Initialize hash table */ + for (i = 0; i < VHOST_FDT_HASHTABLE_SIZE; i++) { + ht = &fdt->hash_table[i]; + ht->av_slots = VHOST_FDT_HASHTABLE_CHAINS; + + /* for each chain under the hash table, set the fd */ + for (j = 0; j < VHOST_FDT_HASHTABLE_CHAINS; j++) { + hc = &ht->chain[j]; + hc->fd = -1; + hc->hash = 0; + hc->readers = 0; + } + } + mk_list_add(&fdt->_head, list); + } + + MK_TLS_SET(mk_tls_vhost_fdt, list); + pthread_mutex_unlock(&server->vhost_fdt_mutex); + + return 0; +} + +int mk_vhost_fdt_worker_exit(struct mk_server *server) +{ + struct mk_list *list; + struct mk_list *head; + struct mk_list *tmp; + struct vhost_fdt_host *fdt; + + if (server->fdt == MK_FALSE) { + return -1; + } + + list = MK_TLS_GET(mk_tls_vhost_fdt); + mk_list_foreach_safe(head, tmp, list) { + fdt = mk_list_entry(head, struct vhost_fdt_host, _head); + mk_list_del(&fdt->_head); + mk_mem_free(fdt); + } + + mk_mem_free(list); + return 0; +} + + +static inline +struct vhost_fdt_hash_table *mk_vhost_fdt_table_lookup(int id, struct mk_vhost *host) +{ + struct mk_list *head; + struct mk_list *list; + struct vhost_fdt_host *fdt_host; + struct vhost_fdt_hash_table *ht = NULL; + + list = MK_TLS_GET(mk_tls_vhost_fdt); + mk_list_foreach(head, list) { + fdt_host = mk_list_entry(head, struct vhost_fdt_host, _head); + if (fdt_host->host == host) { + ht = &fdt_host->hash_table[id]; + return ht; + } + } + + return ht; +} + +static inline +struct vhost_fdt_hash_chain +*mk_vhost_fdt_chain_lookup(unsigned int hash, struct vhost_fdt_hash_table *ht) +{ + int i; + struct vhost_fdt_hash_chain *hc = NULL; + + for (i = 0; i < VHOST_FDT_HASHTABLE_CHAINS; i++) { + hc = &ht->chain[i]; + if (hc->hash == hash) { + return hc; + } + } + + return NULL; +} + + +static inline int mk_vhost_fdt_open(int id, unsigned int hash, + struct mk_http_request *sr, + struct mk_server *server) +{ + int i; + int fd = -1; + struct vhost_fdt_hash_table *ht = NULL; + struct vhost_fdt_hash_chain *hc; + + if (server->fdt == MK_FALSE) { + return open(sr->real_path.data, sr->file_info.flags_read_only); + } + + ht = mk_vhost_fdt_table_lookup(id, sr->host_conf); + if (mk_unlikely(!ht)) { + return open(sr->real_path.data, sr->file_info.flags_read_only); + } + + /* We got the hash table, now look around the chains array */ + hc = mk_vhost_fdt_chain_lookup(hash, ht); + if (hc) { + /* Increment the readers and return the shared FD */ + hc->readers++; + sr->vhost_fdt_id = id; + sr->vhost_fdt_hash = hash; + sr->vhost_fdt_enabled = MK_TRUE; + return hc->fd; + } + + /* + * Get here means that no entry exists in the hash table for the + * requested file descriptor and hash, we must try to open the file + * and register the entry in the table. + */ + fd = open(sr->real_path.data, sr->file_info.flags_read_only); + if (fd == -1) { + return -1; + } + + /* If chains are full, just return the new FD, bad luck... */ + if (ht->av_slots <= 0) { + return fd; + } + + /* Register the new entry in an available slot */ + for (i = 0; i < VHOST_FDT_HASHTABLE_CHAINS; i++) { + hc = &ht->chain[i]; + if (hc->fd == -1) { + hc->fd = fd; + hc->hash = hash; + hc->readers++; + ht->av_slots--; + + sr->vhost_fdt_id = id; + sr->vhost_fdt_hash = hash; + sr->vhost_fdt_enabled = MK_TRUE; + + return fd; + } + } + + return fd; +} + +static inline int mk_vhost_fdt_close(struct mk_http_request *sr, + struct mk_server *server) +{ + int id; + unsigned int hash; + struct vhost_fdt_hash_table *ht = NULL; + struct vhost_fdt_hash_chain *hc; + + if (server->fdt == MK_FALSE || sr->vhost_fdt_enabled == MK_FALSE) { + if (sr->in_file.fd > 0) { + return close(sr->in_file.fd); + } + return -1; + } + + id = sr->vhost_fdt_id; + hash = sr->vhost_fdt_hash; + + ht = mk_vhost_fdt_table_lookup(id, sr->host_conf); + if (mk_unlikely(!ht)) { + return close(sr->in_file.fd); + } + + /* We got the hash table, now look around the chains array */ + hc = mk_vhost_fdt_chain_lookup(hash, ht); + if (hc) { + /* Increment the readers and check if we should close */ + hc->readers--; + sr->vhost_fdt_enabled = MK_FALSE; + + if (hc->readers == 0) { + hc->fd = -1; + hc->hash = 0; + ht->av_slots++; + return close(sr->in_file.fd); + } + else { + return 0; + } + } + return close(sr->in_file.fd); +} + + +int mk_vhost_open(struct mk_http_request *sr, struct mk_server *server) +{ + int id; + int off; + unsigned int hash; + + off = sr->host_conf->documentroot.len; + hash = mk_utils_gen_hash(sr->real_path.data + off, + sr->real_path.len - off); + id = (hash % VHOST_FDT_HASHTABLE_SIZE); + + return mk_vhost_fdt_open(id, hash, sr, server); +} + +int mk_vhost_close(struct mk_http_request *sr, struct mk_server *server) +{ + return mk_vhost_fdt_close(sr, server); +} + +struct mk_vhost_handler *mk_vhost_handler_match(char *match, + void (*cb)(struct mk_http_request *, + void *), + void *data) +{ + int ret; + struct mk_vhost_handler *h; + + h = mk_mem_alloc(sizeof(struct mk_vhost_handler)); + if (!h) { + return NULL; + } + h->name = NULL; + h->cb = cb; + h->data = data; + h->match = mk_mem_alloc(REGEXP_SIZE); + if (!h->match) { + mk_mem_free(h); + return NULL; + } + mk_list_init(&h->params); + + ret = str_to_regex(match, h->match); + if (ret == -1) { + mk_mem_free(h); + return NULL; + } + + return h; +} + +/* + * Open a virtual host configuration file and return a structure with + * definitions. + */ +struct mk_vhost *mk_vhost_read(char *path) +{ + int ret; + char *tmp; + char *host_low; + struct stat checkdir; + struct mk_vhost *host; + struct mk_vhost_alias *new_alias; + struct mk_vhost_error_page *err_page; + struct mk_rconf *cnf; + struct mk_rconf_section *section_host; + struct mk_rconf_section *section_ep; + struct mk_rconf_section *section_handlers; + struct mk_rconf_entry *entry_ep; + struct mk_string_line *entry; + struct mk_list *head, *list, *line; + struct mk_vhost_handler *h_handler; + struct mk_vhost_handler_param *h_param; + + /* Read configuration file */ + cnf = mk_rconf_open(path); + if (!cnf) { + mk_err("Configuration error, aborting."); + exit(EXIT_FAILURE); + } + + /* Read 'HOST' section */ + section_host = mk_rconf_section_get(cnf, "HOST"); + if (!section_host) { + mk_err("Invalid config file %s", path); + return NULL; + } + + /* Alloc configuration node */ + host = mk_mem_alloc_z(sizeof(struct mk_vhost)); + host->config = cnf; + host->file = mk_string_dup(path); + + /* Init list for host name aliases */ + mk_list_init(&host->server_names); + + /* Init list for custom error pages */ + mk_list_init(&host->error_pages); + + /* Init list for content handlers */ + mk_list_init(&host->handlers); + + /* Lookup Servername */ + list = mk_rconf_section_get_key(section_host, "Servername", MK_RCONF_LIST); + if (!list) { + mk_err("Hostname does not contain a Servername"); + exit(EXIT_FAILURE); + } + + mk_list_foreach(head, list) { + entry = mk_list_entry(head, struct mk_string_line, _head); + if (entry->len > MK_HOSTNAME_LEN - 1) { + continue; + } + + /* Hostname to lowercase */ + host_low = mk_string_tolower(entry->val); + + /* Alloc node */ + new_alias = mk_mem_alloc_z(sizeof(struct mk_vhost_alias)); + new_alias->name = mk_mem_alloc_z(entry->len + 1); + strncpy(new_alias->name, host_low, entry->len); + mk_mem_free(host_low); + + new_alias->len = entry->len; + + mk_list_add(&new_alias->_head, &host->server_names); + } + mk_string_split_free(list); + + /* Lookup document root handled by a mk_ptr_t */ + host->documentroot.data = mk_rconf_section_get_key(section_host, + "DocumentRoot", + MK_RCONF_STR); + if (!host->documentroot.data) { + mk_err("Missing DocumentRoot entry on %s file", path); + mk_rconf_free(cnf); + mk_mem_free(host->file); + mk_mem_free(host); + return NULL; + } + + host->documentroot.len = strlen(host->documentroot.data); + + /* Validate document root configured */ + if (stat(host->documentroot.data, &checkdir) == -1) { + mk_err("Invalid path to DocumentRoot in %s", path); + } + else if (!(checkdir.st_mode & S_IFDIR)) { + mk_err("DocumentRoot variable in %s has an invalid directory path", path); + } + + if (mk_list_is_empty(&host->server_names) == 0) { + mk_rconf_free(cnf); + mk_mem_free(host->file); + mk_mem_free(host); + return NULL; + } + + /* Check Virtual Host redirection */ + host->header_redirect.data = NULL; + host->header_redirect.len = 0; + + tmp = mk_rconf_section_get_key(section_host, + "Redirect", + MK_RCONF_STR); + if (tmp) { + host->header_redirect.data = mk_string_dup(tmp); + host->header_redirect.len = strlen(tmp); + mk_mem_free(tmp); + } + + /* Error Pages */ + section_ep = mk_rconf_section_get(cnf, "ERROR_PAGES"); + if (section_ep) { + mk_list_foreach(head, §ion_ep->entries) { + entry_ep = mk_list_entry(head, struct mk_rconf_entry, _head); + + int ep_status = -1; + char *ep_file = NULL; + unsigned long len; + + ep_status = atoi(entry_ep->key); + ep_file = entry_ep->val; + + /* Validate input values */ + if (ep_status < MK_CLIENT_BAD_REQUEST || + ep_status > MK_SERVER_HTTP_VERSION_UNSUP || + ep_file == NULL) { + continue; + } + + /* Alloc error page node */ + err_page = mk_mem_alloc_z(sizeof(struct mk_vhost_error_page)); + err_page->status = ep_status; + err_page->file = mk_string_dup(ep_file); + err_page->real_path = NULL; + mk_string_build(&err_page->real_path, &len, "%s/%s", + host->documentroot.data, err_page->file); + + MK_TRACE("Map error page: status %i -> %s", err_page->status, err_page->file); + + /* Link page to the error page list */ + mk_list_add(&err_page->_head, &host->error_pages); + } + } + + /* Handlers */ + int i; + int params; + struct mk_list *head_line; + + section_handlers = mk_rconf_section_get(cnf, "HANDLERS"); + if (!section_handlers) { + return host; + } + mk_list_foreach(head, §ion_handlers->entries) { + entry_ep = mk_list_entry(head, struct mk_rconf_entry, _head); + if (strncasecmp(entry_ep->key, "Match", strlen(entry_ep->key)) == 0) { + line = mk_string_split_line(entry_ep->val); + if (!line) { + continue; + } + h_handler = mk_mem_alloc(sizeof(struct mk_vhost_handler)); + if (!h_handler) { + exit(EXIT_FAILURE); + } + h_handler->match = mk_mem_alloc(REGEXP_SIZE); + if (!h_handler->match) { + mk_mem_free(h_handler); + exit(EXIT_FAILURE); + } + h_handler->cb = NULL; + mk_list_init(&h_handler->params); + + i = 0; + params = 0; + mk_list_foreach(head_line, line) { + entry = mk_list_entry(head_line, struct mk_string_line, _head); + switch (i) { + case 0: + ret = str_to_regex(entry->val, h_handler->match); + if (ret == -1) { + return NULL; + } + break; + case 1: + h_handler->name = mk_string_dup(entry->val); + break; + default: + /* link parameters */ + h_param = mk_mem_alloc(sizeof(struct mk_vhost_handler_param)); + h_param->p.data = mk_string_dup(entry->val); + h_param->p.len = entry->len; + mk_list_add(&h_param->_head, &h_handler->params); + params++; + }; + i++; + } + h_handler->n_params = params; + mk_string_split_free(line); + + if (i < 2) { + mk_err("[Host Handlers] invalid Match value\n"); + exit(EXIT_FAILURE); + } + mk_list_add(&h_handler->_head, &host->handlers); + } + } + + + return host; +} + +int mk_vhost_map_handlers(struct mk_server *server) +{ + int n = 0; + struct mk_list *head; + struct mk_list *head_handler; + struct mk_vhost *host; + struct mk_vhost_handler *h_handler; + struct mk_plugin *p; + + mk_list_foreach(head, &server->hosts) { + host = mk_list_entry(head, struct mk_vhost, _head); + mk_list_foreach(head_handler, &host->handlers) { + h_handler = mk_list_entry(head_handler, + struct mk_vhost_handler, _head); + + /* Lookup plugin by name */ + p = mk_plugin_lookup(h_handler->name, server); + if (!p) { + mk_err("Plugin '%s' was not loaded", h_handler->name); + continue; + } + + if (p->hooks != MK_PLUGIN_STAGE) { + mk_err("Plugin '%s' is not a handler", h_handler->name); + continue; + } + + h_handler->handler = p; + n++; + } + } + + return n; +} + +void mk_vhost_set_single(char *path, struct mk_server *server) +{ + struct mk_vhost *host; + struct mk_vhost_alias *halias; + struct stat checkdir; + + /* Set the default host */ + host = mk_mem_alloc_z(sizeof(struct mk_vhost)); + mk_list_init(&host->error_pages); + mk_list_init(&host->server_names); + + /* Prepare the unique alias */ + halias = mk_mem_alloc_z(sizeof(struct mk_vhost_alias)); + halias->name = mk_string_dup("127.0.0.1"); + mk_list_add(&halias->_head, &host->server_names); + + host->documentroot.data = mk_string_dup(path); + host->documentroot.len = strlen(path); + host->header_redirect.data = NULL; + + /* Validate document root configured */ + if (stat(host->documentroot.data, &checkdir) == -1) { + mk_err("Invalid path to DocumentRoot in %s", path); + exit(EXIT_FAILURE); + } + else if (!(checkdir.st_mode & S_IFDIR)) { + mk_err("DocumentRoot variable in %s has an invalid directory path", path); + exit(EXIT_FAILURE); + } + mk_list_add(&host->_head, &server->hosts); + mk_list_init(&host->handlers); +} + +/* Given a configuration directory, start reading the virtual host entries */ +void mk_vhost_init(char *path, struct mk_server *server) +{ + DIR *dir; + unsigned long len; + char *buf = 0; + char *sites = 0; + char *file; + struct mk_vhost *p_host; /* debug */ + struct dirent *ent; + struct file_info f_info; + int ret; + + if (!server->conf_sites) { + mk_warn("[vhost] skipping default site"); + return; + } + + /* Read default virtual host file */ + mk_string_build(&sites, &len, "%s/%s/", + path, server->conf_sites); + ret = mk_file_get_info(sites, &f_info, MK_FILE_EXISTS); + if (ret == -1 || f_info.is_directory == MK_FALSE) { + mk_mem_free(sites); + sites = server->conf_sites; + } + + mk_string_build(&buf, &len, "%s/default", sites); + + p_host = mk_vhost_read(buf); + if (!p_host) { + mk_err("Error parsing main configuration file 'default'"); + } + mk_list_add(&p_host->_head, &server->hosts); + server->nhosts++; + mk_mem_free(buf); + buf = NULL; + + + /* Read all virtual hosts defined in sites/ */ + if (!(dir = opendir(sites))) { + mk_mem_free(sites); + mk_err("Could not open %s", sites); + exit(EXIT_FAILURE); + } + + /* Reading content */ + while ((ent = readdir(dir)) != NULL) { + if (ent->d_name[0] == '.') { + continue; + } + if (strcmp((char *) ent->d_name, "..") == 0) { + continue; + } + if (ent->d_name[strlen(ent->d_name) - 1] == '~') { + continue; + } + if (strcasecmp((char *) ent->d_name, "default") == 0) { + continue; + } + file = NULL; + mk_string_build(&file, &len, "%s/%s", sites, ent->d_name); + + p_host = mk_vhost_read(file); + mk_mem_free(file); + if (!p_host) { + continue; + } + else { + mk_list_add(&p_host->_head, &server->hosts); + server->nhosts++; + } + } + closedir(dir); + mk_mem_free(sites); +} + + +/* Lookup a registered virtual host based on the given 'host' input */ +int mk_vhost_get(mk_ptr_t host, struct mk_vhost **vhost, + struct mk_vhost_alias **alias, + struct mk_server *server) +{ + struct mk_vhost *entry_host; + struct mk_vhost_alias *entry_alias; + struct mk_list *head_vhost, *head_alias; + + mk_list_foreach(head_vhost, &server->hosts) { + entry_host = mk_list_entry(head_vhost, struct mk_vhost, _head); + mk_list_foreach(head_alias, &entry_host->server_names) { + entry_alias = mk_list_entry(head_alias, struct mk_vhost_alias, _head); + if (entry_alias->len == host.len && + strncmp(entry_alias->name, host.data, host.len) == 0) { + *vhost = entry_host; + *alias = entry_alias; + return 0; + } + } + } + + return -1; +} + +static void mk_vhost_handler_free(struct mk_vhost_handler *h) +{ + struct mk_list *tmp; + struct mk_list *head; + struct mk_vhost_handler_param *param; + + /* Release Params */ + mk_list_foreach_safe(head, tmp, &h->params) { + param = mk_list_entry(head, struct mk_vhost_handler_param, _head); + mk_list_del(¶m->_head); + mk_mem_free(param->p.data); + mk_mem_free(param); + } + + mk_mem_free(h->match); + mk_mem_free(h->name); + mk_mem_free(h); +} + +int mk_vhost_destroy(struct mk_vhost *vh) +{ + struct mk_vhost_alias *halias = NULL; + struct mk_vhost_handler *hhandler; + struct mk_vhost_error_page *ep; + struct mk_list *head; + struct mk_list *tmp; + + if (vh) { + /* Free aliases or servernames */ + mk_list_foreach_safe(head, tmp, &vh->server_names) { + halias = mk_list_entry(head, struct mk_vhost_alias, _head); + if (halias) { + mk_list_del(&halias->_head); + if (halias->name) { + mk_mem_free(halias->name); + } + mk_mem_free(halias); + } + } + + /* Handlers */ + mk_list_foreach_safe(head, tmp, &vh->handlers) { + hhandler = mk_list_entry(head, struct mk_vhost_handler, _head); + if (hhandler) { + mk_vhost_handler_free(hhandler); + } + } + + /* Free error pages */ + mk_list_foreach_safe(head, tmp, &vh->error_pages) { + ep = mk_list_entry(head, struct mk_vhost_error_page, _head); + if (ep) { + mk_list_del(&ep->_head); + if (ep->file) { + mk_mem_free(ep->file); + } + if (ep->real_path) { + mk_mem_free(ep->real_path); + } + mk_mem_free(ep); + } + } + mk_ptr_free(&vh->documentroot); + + /* Free source configuration */ + if (vh->config) { + mk_rconf_free(vh->config); + } + mk_list_del(&vh->_head); + if (vh->file) { + mk_mem_free(vh->file); + } + + mk_mem_free(vh); + } + return 0; +} + +void mk_vhost_free_all(struct mk_server *server) +{ + struct mk_vhost *host; + struct mk_list *head; + struct mk_list *tmp; + + mk_list_foreach_safe(head, tmp, &server->hosts) { + host = mk_list_entry(head, struct mk_vhost, _head); + mk_vhost_destroy(host); + } +} -- cgit v1.2.3