summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/monkey/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-03-09 13:19:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-03-09 13:19:22 +0000
commitc21c3b0befeb46a51b6bf3758ffa30813bea0ff0 (patch)
tree9754ff1ca740f6346cf8483ec915d4054bc5da2d /fluent-bit/lib/monkey/plugins
parentAdding upstream version 1.43.2. (diff)
downloadnetdata-c21c3b0befeb46a51b6bf3758ffa30813bea0ff0.tar.xz
netdata-c21c3b0befeb46a51b6bf3758ffa30813bea0ff0.zip
Adding upstream version 1.44.3.upstream/1.44.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'fluent-bit/lib/monkey/plugins')
-rw-r--r--fluent-bit/lib/monkey/plugins/CMakeLists.txt111
-rw-r--r--fluent-bit/lib/monkey/plugins/README61
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/ABOUT2
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/CMakeLists.txt10
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/OPTIONAL0
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/auth.c254
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/auth.h106
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/base64.c172
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/base64.h11
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/conf.c244
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/conf.h25
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/sha1.c288
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/sha1.h22
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/tools/CMakeLists.txt15
-rw-r--r--fluent-bit/lib/monkey/plugins/auth/tools/mk_passwd.c209
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/CMakeLists.txt7
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/cgi.c501
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/cgi.h133
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/event.c170
-rw-r--r--fluent-bit/lib/monkey/plugins/cgi/request.c72
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/ABOUT4
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/CMakeLists.txt9
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/OPTIONAL0
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/cheetah.c162
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/cheetah.h79
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/cmd.c472
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/cmd.h41
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/conf/CMakeLists.txt9
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/conf/cheetah.conf17
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/cutils.c117
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/cutils.h32
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/loop.c149
-rw-r--r--fluent-bit/lib/monkey/plugins/cheetah/loop.h26
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/ABOUT4
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/CMakeLists.txt7
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/OPTIONAL0
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/conf/CMakeLists.txt11
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/conf/dirhtml.conf2
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/entry.theme5
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/footer.theme8
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/header.theme27
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/entry.theme14
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/footer.theme7
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/header.theme58
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/dirlisting.c949
-rw-r--r--fluent-bit/lib/monkey/plugins/dirlisting/dirlisting.h181
-rw-r--r--fluent-bit/lib/monkey/plugins/fastcgi/CMakeLists.txt7
-rw-r--r--fluent-bit/lib/monkey/plugins/fastcgi/conf/CMakeLists.txt9
-rw-r--r--fluent-bit/lib/monkey/plugins/fastcgi/conf/fastcgi.conf16
-rw-r--r--fluent-bit/lib/monkey/plugins/fastcgi/fastcgi.c233
-rw-r--r--fluent-bit/lib/monkey/plugins/fastcgi/fastcgi.h36
-rw-r--r--fluent-bit/lib/monkey/plugins/fastcgi/fcgi_handler.c967
-rw-r--r--fluent-bit/lib/monkey/plugins/fastcgi/fcgi_handler.h128
-rw-r--r--fluent-bit/lib/monkey/plugins/liana/ABOUT4
-rw-r--r--fluent-bit/lib/monkey/plugins/liana/CMakeLists.txt5
-rw-r--r--fluent-bit/lib/monkey/plugins/liana/MANDATORY0
-rw-r--r--fluent-bit/lib/monkey/plugins/liana/Makefile.in18
-rw-r--r--fluent-bit/lib/monkey/plugins/liana/README7
-rw-r--r--fluent-bit/lib/monkey/plugins/liana/STATIC0
-rw-r--r--fluent-bit/lib/monkey/plugins/liana/liana.c221
-rw-r--r--fluent-bit/lib/monkey/plugins/logger/CMakeLists.txt21
-rw-r--r--fluent-bit/lib/monkey/plugins/logger/conf/CMakeLists.txt11
-rw-r--r--fluent-bit/lib/monkey/plugins/logger/conf/logger.conf.in22
-rw-r--r--fluent-bit/lib/monkey/plugins/logger/logger.c852
-rw-r--r--fluent-bit/lib/monkey/plugins/logger/logger.h58
-rw-r--r--fluent-bit/lib/monkey/plugins/logger/pointers.c43
-rw-r--r--fluent-bit/lib/monkey/plugins/logger/pointers.h59
-rw-r--r--fluent-bit/lib/monkey/plugins/mandril/CMakeLists.txt6
-rw-r--r--fluent-bit/lib/monkey/plugins/mandril/conf/CMakeLists.txt9
-rw-r--r--fluent-bit/lib/monkey/plugins/mandril/conf/mandril.conf57
-rw-r--r--fluent-bit/lib/monkey/plugins/mandril/mandril.c403
-rw-r--r--fluent-bit/lib/monkey/plugins/mandril/mandril.h57
-rw-r--r--fluent-bit/lib/monkey/plugins/tls/CMakeLists.txt17
-rw-r--r--fluent-bit/lib/monkey/plugins/tls/conf/CMakeLists.txt9
-rw-r--r--fluent-bit/lib/monkey/plugins/tls/conf/tls.conf25
-rw-r--r--fluent-bit/lib/monkey/plugins/tls/tls.c862
76 files changed, 8965 insertions, 0 deletions
diff --git a/fluent-bit/lib/monkey/plugins/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/CMakeLists.txt
new file mode 100644
index 000000000..a78d83695
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/CMakeLists.txt
@@ -0,0 +1,111 @@
+set(static_plugins "" CACHE INTERNAL "static_plugins")
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+# CHECK_STATIC_PLUGIN: Check if a plugin will be linked statically
+macro(CHECK_STATIC_PLUGIN name)
+ string(REPLACE "," ";" plugins ${MK_STATIC_PLUGINS})
+ list(FIND plugins ${name} found)
+ if(NOT found EQUAL -1)
+ set(IS_STATIC TRUE)
+ else()
+ set(IS_STATIC FALSE)
+ endif()
+endmacro()
+
+# MONKEY_PLUGIN: Used by plugins to register and create the targets
+macro(MONKEY_PLUGIN name src)
+ CHECK_STATIC_PLUGIN(${name})
+ if(IS_STATIC)
+ add_library(monkey-${name}-static STATIC ${src})
+ set_target_properties(monkey-${name}-static PROPERTIES OUTPUT_NAME monkey-${name})
+ set_target_properties(monkey-${name}-static PROPERTIES PREFIX "")
+ else()
+ if(APPLE)
+ add_library(monkey-${name}-shared MODULE ${src})
+ else()
+ add_library(monkey-${name}-shared SHARED ${src})
+ endif()
+ set_target_properties(monkey-${name}-shared PROPERTIES OUTPUT_NAME monkey-${name})
+ set_target_properties(monkey-${name}-shared PROPERTIES PREFIX "")
+
+ if(NOT MK_LOCAL)
+ if(CMAKE_INSTALL_LIBDIR)
+ install(TARGETS monkey-${name}-shared LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+ else()
+ install(TARGETS monkey-${name}-shared LIBRARY DESTINATION lib)
+ endif()
+ endif()
+ endif()
+endmacro()
+
+# MK_BUILD_PLUGIN: This macro determinate if the plugin is enabled through the
+# option MK_PLUGIN_NAME defined on the root CMakeLists.txt
+macro(MK_BUILD_PLUGIN name)
+ set(mode "")
+ string(TOUPPER ${name} NAME)
+
+ # Check if the plugin is enabled
+ set(option MK_PLUGIN_${NAME})
+ if(${option})
+ add_subdirectory(${name})
+
+ # Is this a static plugin ?
+ CHECK_STATIC_PLUGIN(${name})
+ if(IS_STATIC)
+ # Let Monkey and CMake aware about this is a static plugin. A static plugin
+ # requires a different handling: link the object and register the plugin
+ # struct reference on mk_static_plugins.h
+ set(static_plugins "${static_plugins}monkey-${name}-static;")
+ set(STATIC_PLUGINS_INIT "${STATIC_PLUGINS_INIT}\n mk_static_plugin_attach(plugins, &mk_plugin_${name});\n")
+ set(STATIC_PLUGINS_DECL "${STATIC_PLUGINS_DECL}extern struct mk_plugin mk_plugin_${name};\n")
+
+ # append message to stdout
+ set(mode "[== static ==]")
+ else()
+ if(MK_LOCAL)
+ set(MK_LOAD_PLUGINS "${MK_LOAD_PLUGINS} # Load ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/monkey-${name}.so\n")
+ else()
+ set(MK_LOAD_PLUGINS "${MK_LOAD_PLUGINS} # Load ${CMAKE_INSTALL_FULL_LIBDIR}/monkey-${name}.so\n")
+ endif()
+ endif()
+ message(STATUS "Plugin ${name} enabled ${mode}")
+ endif()
+endmacro()
+
+
+macro(MONKEY_PLUGIN_LINK_LIB target lib)
+ CHECK_STATIC_PLUGIN(${target})
+ if(IS_STATIC)
+ target_link_libraries(monkey-${target}-static ${lib})
+ else()
+ target_link_libraries(monkey-${target}-shared ${lib})
+ endif()
+endmacro()
+
+# Try to configure/build all plugins
+MK_BUILD_PLUGIN("auth")
+MK_BUILD_PLUGIN("cgi")
+MK_BUILD_PLUGIN("cheetah")
+MK_BUILD_PLUGIN("dirlisting")
+MK_BUILD_PLUGIN("fastcgi")
+MK_BUILD_PLUGIN("liana")
+MK_BUILD_PLUGIN("logger")
+MK_BUILD_PLUGIN("mandril")
+MK_BUILD_PLUGIN("tls")
+MK_BUILD_PLUGIN("duda")
+
+# Generate include/monkey/mk_static_plugins.h
+configure_file(
+ "${PROJECT_SOURCE_DIR}/include/monkey/mk_static_plugins.h.in"
+ "${PROJECT_SOURCE_DIR}/include/monkey/mk_static_plugins.h"
+ )
+
+# Generate conf/plugins.load
+if(NOT MK_WITHOUT_CONF)
+ configure_file(
+ "${PROJECT_SOURCE_DIR}/conf/plugins.load.in"
+ "${PROJECT_BINARY_DIR}/conf/plugins.load"
+ )
+endif()
+
+set(STATIC_PLUGINS_LIBS "${static_plugins}" PARENT_SCOPE)
diff --git a/fluent-bit/lib/monkey/plugins/README b/fluent-bit/lib/monkey/plugins/README
new file mode 100644
index 000000000..0daa023cb
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/README
@@ -0,0 +1,61 @@
+Monkey Plugins
+==============
+Plugins are extra features which modifies the Monkey behavior, all of them
+are shared libraries which are loaded on runtime.
+
+Each plugin is loaded per configuration instruction and it will work on a
+defined stage or event depending of it's type.
+
+Please review the file API.txt for more details
+
+
+
+MK_PLUGIN_STAGE_10: Server has not yet entered in the server loop, no
+ listeners yet available
+---------------------------------------------------------------------
+ Return Values >
+
+
+MK_PLUGIN_STAGE_20: Accepted connection has not been assigned to worker thread
+------------------------------------------------------------------------------
+ Return Values >
+
+ * MK_PLUGIN_RET_CLOSE_CONX: The connection must be closed.
+
+
+MK_PLUGIN_STAGE_30: HTTP Request received
+-----------------------------------------
+ Return Values >
+ * MK_PLUGIN_RET_CLOSE_CONX: The connection must be closed.
+
+
+MK_PLUGIN_STAGE_40: Object Handler
+-----------------------------------------
+ Extra functions >
+ * _mk_plugin_stage_40_loop(): if _mk_plugin_stage_40() has
+ returned MK_PLUGIN_RET_CONTINUE, the server will wait
+ for an event and call _mk_plugin_stage_40() until it
+ returns MK_PLUGIN_RET_END.
+
+ Return Values >
+ * MK_PLUGIN_RET_END
+ * MK_PLUGIN_RET_CONTINUE
+
+ Return Values >
+ * MK_PLUGIN_RET_NOT_ME: Plugin will not handle this request.
+
+ * MK_PLUGIN_RET_END: Plugin has taken some action and
+ has finished the work, the handler will no take the request
+ again.
+
+ * MK_PLUGIN_RET_CONTINUE:: Plugin has taken some action and
+ will continue in the next loop.
+
+
+MK_PLUGIN_STAGE_50: Request ended
+-----------------------------------------
+
+
+MK_PLUGIN_STAGE_60: The Connection has been closed
+--------------------------------------------------
+
diff --git a/fluent-bit/lib/monkey/plugins/auth/ABOUT b/fluent-bit/lib/monkey/plugins/auth/ABOUT
new file mode 100644
index 000000000..66a5384c3
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/ABOUT
@@ -0,0 +1,2 @@
+HTTP Basic Authentication
+=========================
diff --git a/fluent-bit/lib/monkey/plugins/auth/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/auth/CMakeLists.txt
new file mode 100644
index 000000000..a6fd27a06
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(src
+ auth.c
+ base64.c
+ conf.c
+ sha1.c
+ )
+
+add_subdirectory(tools)
+
+MONKEY_PLUGIN(auth "${src}")
diff --git a/fluent-bit/lib/monkey/plugins/auth/OPTIONAL b/fluent-bit/lib/monkey/plugins/auth/OPTIONAL
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/OPTIONAL
diff --git a/fluent-bit/lib/monkey/plugins/auth/auth.c b/fluent-bit/lib/monkey/plugins/auth/auth.c
new file mode 100644
index 000000000..b0f983bfb
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/auth.c
@@ -0,0 +1,254 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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 <monkey/mk_api.h>
+
+#include <sys/stat.h>
+
+#include "auth.h"
+#include "conf.h"
+#include "sha1.h"
+#include "base64.h"
+
+static int mk_auth_validate_user(struct users_file *users,
+ const char *credentials, unsigned int len)
+{
+ int sep;
+ size_t auth_len;
+ unsigned char *decoded = NULL;
+ unsigned char digest[SHA1_DIGEST_LEN];
+ struct mk_list *head;
+ struct user *entry;
+
+ SHA_CTX sha; /* defined in sha1/sha1.h */
+
+ /* Validate value length */
+ if (len <= auth_header_basic.len + 1) {
+ return -1;
+ }
+
+ /* Validate 'basic' credential type */
+ if (strncmp(credentials, auth_header_basic.data,
+ auth_header_basic.len) != 0) {
+ return -1;
+ }
+
+ /* Decode credentials: incoming credentials comes in base64 encode */
+ decoded = base64_decode((unsigned char *) credentials + auth_header_basic.len,
+ len - auth_header_basic.len,
+ &auth_len);
+ if (decoded == NULL) {
+ PLUGIN_TRACE("Failed to decode credentials.");
+ goto error;
+ }
+
+ if (auth_len <= 3) {
+ goto error;
+ }
+
+ sep = mk_api->str_search_n((char *) decoded, ":", 1, auth_len);
+ if (sep == -1 || sep == 0 || (unsigned int) sep == auth_len - 1) {
+ goto error;
+ }
+
+ /* Get SHA1 hash */
+ SHA1_Init(&sha);
+ SHA1_Update(&sha, (unsigned char *) decoded + sep + 1, auth_len - (sep + 1));
+ SHA1_Final(digest, &sha);
+
+ mk_list_foreach(head, &users->_users) {
+ entry = mk_list_entry(head, struct user, _head);
+ /* match user */
+ if (strlen(entry->user) != (unsigned int) sep) {
+ continue;
+ }
+ if (strncmp(entry->user, (char *) decoded, sep) != 0) {
+ continue;
+ }
+
+ PLUGIN_TRACE("User match '%s'", entry->user);
+
+ /* match password */
+ if (memcmp(entry->passwd_decoded, digest, SHA1_DIGEST_LEN) == 0) {
+ PLUGIN_TRACE("User '%s' matched password", entry->user);
+ mk_api->mem_free(decoded);
+ return 0;
+ }
+ PLUGIN_TRACE("Invalid password");
+ break;
+ }
+
+ error:
+ if (decoded) {
+ mk_api->mem_free(decoded);
+ }
+ return -1;
+}
+
+int mk_auth_plugin_init(struct plugin_api **api, char *confdir)
+{
+ (void) confdir;
+
+ mk_api = *api;
+
+ /* Init and load global users list */
+ mk_list_init(&vhosts_list);
+ mk_list_init(&users_file_list);
+ mk_auth_conf_init_users_list();
+
+ /* Set HTTP headers key */
+ auth_header_basic.data = MK_AUTH_HEADER_BASIC;
+ auth_header_basic.len = sizeof(MK_AUTH_HEADER_BASIC) - 1;
+
+ return 0;
+}
+
+int mk_auth_plugin_exit()
+{
+ return 0;
+}
+
+void mk_auth_worker_init()
+{
+ char *user;
+
+ /* Init thread buffer for given credentials */
+ user = mk_api->mem_alloc(MK_AUTH_CREDENTIALS_LEN - 1);
+ pthread_setspecific(_mkp_data, (void *) user);
+}
+
+/* Object handler */
+int mk_auth_stage30(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ int n_params,
+ struct mk_list *params)
+{
+ int val;
+ short int is_restricted = MK_FALSE;
+ struct mk_list *vh_head;
+ struct mk_list *loc_head;
+ struct vhost *vh_entry = NULL;
+ struct location *loc_entry;
+ struct mk_http_header *header;
+ (void) plugin;
+ (void) n_params;
+ (void) params;
+
+ PLUGIN_TRACE("[FD %i] Handler received request");
+
+ /* Match auth_vhost with global vhost */
+ mk_list_foreach(vh_head, &vhosts_list) {
+ vh_entry = mk_list_entry(vh_head, struct vhost, _head);
+ if (vh_entry->host == sr->host_conf) {
+ PLUGIN_TRACE("[FD %i] host matched %s",
+ cs->socket,
+ mk_api->config->server_signature);
+ break;
+ }
+ }
+
+ if (!vh_entry) {
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+
+ /* Check vhost locations */
+ mk_list_foreach(loc_head, &vh_entry->locations) {
+ loc_entry = mk_list_entry(loc_head, struct location, _head);
+ if (sr->uri_processed.len < loc_entry->path.len) {
+ continue;
+ }
+ if (strncmp(sr->uri_processed.data,
+ loc_entry->path.data, loc_entry->path.len) == 0) {
+ is_restricted = MK_TRUE;
+ PLUGIN_TRACE("[FD %i] Location matched %s",
+ cs->socket,
+ loc_entry->path.data);
+ break;
+ }
+ }
+
+ /* For non-restricted location do not take any action, just returns */
+ if (is_restricted == MK_FALSE) {
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+
+ /* Check authorization header */
+ header = mk_api->header_get(MK_HEADER_AUTHORIZATION,
+ sr, NULL, 0);
+
+ if (header) {
+ /* Validate user */
+ val = mk_auth_validate_user(loc_entry->users,
+ header->val.data, header->val.len);
+ if (val == 0) {
+ /* user validated, success */
+ PLUGIN_TRACE("[FD %i] user validated!", cs->socket);
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+ }
+
+ /* Restricted access: requires auth */
+ PLUGIN_TRACE("[FD %i] unauthorized user, credentials required",
+ cs->socket);
+
+ sr->headers.content_length = 0;
+ mk_api->header_set_http_status(sr, MK_CLIENT_UNAUTH);
+ mk_api->header_add(sr,
+ loc_entry->auth_http_header.data,
+ loc_entry->auth_http_header.len);
+
+ mk_api->header_prepare(plugin, cs, sr);
+ return MK_PLUGIN_RET_END;
+}
+
+int mk_auth_stage30_hangup(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr)
+{
+ (void) plugin;
+ (void) cs;
+ (void) sr;
+
+ return 0;
+}
+
+struct mk_plugin_stage mk_plugin_stage_auth = {
+ .stage30 = &mk_auth_stage30,
+ .stage30_hangup = &mk_auth_stage30_hangup
+};
+
+struct mk_plugin mk_plugin_auth = {
+ /* Identification */
+ .shortname = "auth",
+ .name = "Basic Authentication",
+ .version = MK_VERSION_STR,
+ .hooks = MK_PLUGIN_STAGE,
+
+ /* Init / Exit */
+ .init_plugin = mk_auth_plugin_init,
+ .exit_plugin = mk_auth_plugin_exit,
+
+ /* Init Levels */
+ .master_init = NULL,
+ .worker_init = mk_auth_worker_init,
+
+ /* Type */
+ .stage = &mk_plugin_stage_auth
+};
diff --git a/fluent-bit/lib/monkey/plugins/auth/auth.h b/fluent-bit/lib/monkey/plugins/auth/auth.h
new file mode 100644
index 000000000..f6eebe83e
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/auth.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#ifndef MK_AUTH_H
+#define MK_AUTH_H
+
+#include <monkey/mk_api.h>
+
+/* Header stuff */
+#define MK_AUTH_HEADER_BASIC "Basic "
+#define MK_AUTH_HEADER_TITLE "WWW-Authenticate: Basic realm=\"%s\""
+
+/* Credentials length */
+#define MK_AUTH_CREDENTIALS_LEN 256
+
+/*
+ * The plugin hold one struct per virtual host and link to the
+ * locations and users file associated:
+ *
+ * +---------------------------------+
+ * struct vhost > vhost (1:N) |
+ * | +---------+----------+ |
+ * | | | | |
+ * struct location > location location location |
+ * | | | | |
+ * | +----+----+ + |
+ * | | | |
+ * struct users > users users |
+ * +---------------------------------+
+ *
+ */
+
+/* List of virtual hosts to handle locations */
+struct mk_list vhosts_list;
+
+/* main index for locations under a virtualhost */
+struct vhost {
+ struct mk_vhost *host;
+ struct mk_list locations;
+ struct mk_list _head;
+};
+
+/*
+ * A location restrict a filesystem path with a list
+ * of allowed users
+ */
+struct location {
+ mk_ptr_t path;
+ mk_ptr_t title;
+ mk_ptr_t auth_http_header;
+
+ struct users_file *users;
+ struct mk_list _head;
+};
+
+/* Head index for user files list */
+struct mk_list users_file_list;
+
+/*
+ * Represents a users file, each entry represents a physical
+ * file and belongs to a node of the users_file_list list
+ */
+struct users_file {
+ time_t last_updated; /* last time this entry was modified */
+ char *path; /* file path */
+ struct mk_list _users; /* list of users */
+ struct mk_list _head; /* head for main mk_list users_file_list */
+};
+
+/*
+ * a list of users, this list belongs to a
+ * struct location
+ */
+struct user {
+ char user[128];
+ char passwd_raw[256];
+ unsigned char *passwd_decoded;
+
+ struct mk_list _head;
+};
+
+struct mk_list users_file_list;
+
+/* Thread key */
+mk_ptr_t auth_header_request;
+mk_ptr_t auth_header_basic;
+
+#define SHA1_DIGEST_LEN 20
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/auth/base64.c b/fluent-bit/lib/monkey/plugins/auth/base64.c
new file mode 100644
index 000000000..e3149fe73
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/base64.c
@@ -0,0 +1,172 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifdef _FORCE_SYSMALLOC
+#undef MALLOC_JEMALLOC
+#endif
+
+#include <monkey/mk_api.h>
+#include "base64.h"
+
+#if defined(MALLOC_JEMALLOC)
+#define __mem_alloc mk_api->mem_alloc
+#define __mem_free mk_api->mem_free
+#else
+#define __mem_alloc malloc
+#define __mem_free free
+#endif
+
+static const unsigned char base64_table[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char *out, *pos;
+ const unsigned char *end, *in;
+ size_t olen;
+ int line_len;
+
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+ olen += olen / 72; /* line feeds */
+ olen++; /* nul termination */
+ if (olen < len)
+ return NULL; /* integer overflow */
+ if (mk_api != NULL) {
+ out = __mem_alloc(olen);
+ }
+ else {
+ out = __mem_alloc(olen);
+ }
+
+ if (out == NULL)
+ return NULL;
+
+ end = src + len;
+ in = src;
+ pos = out;
+ line_len = 0;
+ while (end - in >= 3) {
+ *pos++ = base64_table[in[0] >> 2];
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+ *pos++ = base64_table[in[2] & 0x3f];
+ in += 3;
+ line_len += 4;
+ if (line_len >= 72) {
+ *pos++ = '\n';
+ line_len = 0;
+ }
+ }
+
+ if (end - in) {
+ *pos++ = base64_table[in[0] >> 2];
+ if (end - in == 1) {
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
+ *pos++ = '=';
+ } else {
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
+ (in[1] >> 4)];
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
+ }
+ *pos++ = '=';
+ line_len += 4;
+ }
+
+ if (line_len)
+ *pos++ = '\n';
+
+ *pos = '\0';
+ if (out_len)
+ *out_len = pos - out;
+ return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len)
+{
+ unsigned char dtable[256], *out, *pos, block[4], tmp;
+ size_t i, count, olen;
+ int pad = 0;
+
+ memset(dtable, 0x80, 256);
+ for (i = 0; i < sizeof(base64_table) - 1; i++)
+ dtable[base64_table[i]] = (unsigned char) i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0 || count % 4)
+ return NULL;
+
+ olen = (count / 4 * 3) + 1;
+ pos = out = __mem_alloc(olen);
+ if (out == NULL)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ tmp = dtable[src[i]];
+ if (tmp == 0x80)
+ continue;
+
+ if (src[i] == '=')
+ pad++;
+ block[count] = tmp;
+ count++;
+ if (count == 4) {
+ *pos++ = (block[0] << 2) | (block[1] >> 4);
+ *pos++ = (block[1] << 4) | (block[2] >> 2);
+ *pos++ = (block[2] << 6) | block[3];
+ count = 0;
+ if (pad) {
+ if (pad == 1)
+ pos--;
+ else if (pad == 2)
+ pos -= 2;
+ else {
+ /* Invalid padding */
+ __mem_free(out);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+ *pos = '\0';
+
+ *out_len = pos - out;
+ return out;
+}
diff --git a/fluent-bit/lib/monkey/plugins/auth/base64.h b/fluent-bit/lib/monkey/plugins/auth/base64.h
new file mode 100644
index 000000000..45001d47f
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/base64.h
@@ -0,0 +1,11 @@
+#ifndef AUTH_BASE64_H
+#define AUTH_BASE64_H
+
+
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+ size_t *out_len);
+unsigned char *base64_decode(const unsigned char *src, size_t len,
+ size_t *out_len);
+
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/auth/conf.c b/fluent-bit/lib/monkey/plugins/auth/conf.c
new file mode 100644
index 000000000..6dede1dd4
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/conf.c
@@ -0,0 +1,244 @@
+ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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 <monkey/mk_api.h>
+#include "base64.h"
+#include "auth.h"
+#include "conf.h"
+
+/*
+ * Register a users file into the main list, if the users
+ * file already exists it just return the node in question,
+ * otherwise add the node to the list and return the node
+ * created.
+ */
+static struct users_file *mk_auth_conf_add_users(char *users_path)
+{
+ struct file_info finfo;
+ struct mk_list *head;
+ struct users_file *entry;
+ struct user *cred;
+ int i, sep, len;
+ int offset = 0;
+ size_t decoded_len;
+ char *buf;
+
+ mk_list_foreach(head, &users_file_list) {
+ entry = mk_list_entry(head, struct users_file, _head);
+ if (strcmp(entry->path, users_path) == 0) {
+ return entry;
+ }
+ }
+
+ if (mk_api->file_get_info(users_path, &finfo, MK_FILE_READ) != 0) {
+ mk_warn("Auth: Invalid users file '%s'", users_path);
+ return NULL;
+ }
+
+ if (finfo.is_directory == MK_TRUE) {
+ mk_warn("Auth: Not a credentials file '%s'", users_path);
+ return NULL;
+ }
+
+ if (finfo.read_access == MK_FALSE) {
+ mk_warn("Auth: Could not read file '%s'", users_path);
+ return NULL;
+ }
+
+ /* We did not find the path in our list, let's create a new node */
+ entry = mk_api->mem_alloc(sizeof(struct users_file));
+ entry->last_updated = finfo.last_modification;
+ entry->path = users_path;
+
+ /* Read file and add users to the list */
+ mk_list_init(&entry->_users);
+
+ /* Read credentials file */
+ buf = mk_api->file_to_buffer(users_path);
+ if (!buf) {
+ mk_warn("Auth: No users loaded '%s'", users_path);
+ return NULL;
+ }
+
+ /* Read users list buffer lines */
+ len = strlen(buf);
+ for (i = 0; i < len; i++) {
+ if (buf[i] == '\n' || (i) == len -1) {
+ sep = mk_api->str_search(buf + offset, ":", 1);
+
+ if (sep >= (int)sizeof(cred->user)) {
+ mk_warn("Auth: username too long");
+ offset = i + 1;
+ continue;
+ }
+ if (i - offset - sep - 1 - 5 >= (int)sizeof(cred->passwd_raw)) {
+ mk_warn("Auth: password hash too long");
+ offset = i + 1;
+ continue;
+ }
+
+ cred = mk_api->mem_alloc(sizeof(struct user));
+
+ /* Copy username */
+ strncpy(cred->user, buf + offset, sep);
+ cred->user[sep] = '\0';
+
+ /* Copy raw password */
+ offset += sep + 1 + 5;
+ strncpy(cred->passwd_raw,
+ buf + offset,
+ i - (offset));
+ cred->passwd_raw[i - offset] = '\0';
+
+ /* Decode raw password */
+ cred->passwd_decoded = base64_decode((unsigned char *)(cred->passwd_raw),
+ strlen(cred->passwd_raw),
+ &decoded_len);
+
+ offset = i + 1;
+
+ if (!cred->passwd_decoded) {
+ mk_warn("Auth: invalid user '%s' in '%s'",
+ cred->user, users_path);
+ mk_api->mem_free(cred);
+ continue;
+ }
+ mk_list_add(&cred->_head, &entry->_users);
+ }
+ }
+ mk_api->mem_free(buf);
+
+ /* Link node to global list */
+ mk_list_add(&entry->_head, &users_file_list);
+
+ return entry;
+}
+
+/*
+ * Read all vhost configuration nodes and looks for users files under an [AUTH]
+ * section, if present, it add that file to the unique list. It parse all user's
+ * files mentioned to avoid duplicated lists in memory.
+ */
+int mk_auth_conf_init_users_list()
+{
+ /* Section data */
+ char *location;
+ char *title;
+ char *users_path;
+ /* auth vhost list */
+ struct vhost *auth_vhost;
+
+ /* vhost configuration */
+ struct mk_list *head_hosts;
+ struct mk_list *hosts = &mk_api->config->hosts;
+ struct mk_list *head_sections;
+ struct mk_vhost *entry_host;
+ struct mk_rconf_section *section;
+
+ /* vhost [AUTH] locations */
+ struct location *loc;
+
+ /* User files list */
+ struct users_file *uf;
+
+ PLUGIN_TRACE("Loading user's files");
+
+ mk_list_foreach(head_hosts, hosts) {
+ entry_host = mk_list_entry(head_hosts, struct mk_vhost, _head);
+ if (!entry_host->config) {
+ continue;
+ }
+
+ auth_vhost = mk_api->mem_alloc(sizeof(struct vhost));
+ auth_vhost->host = entry_host; /* link virtual host entry */
+ mk_list_init(&auth_vhost->locations); /* init locations list */
+
+ /*
+ * check vhost 'config' and look for [AUTH] sections, we don't use
+ * mk_config_section_get() because we can have multiple [AUTH]
+ * sections.
+ */
+ mk_list_foreach(head_sections, &entry_host->config->sections) {
+ section = mk_list_entry(head_sections, struct mk_rconf_section, _head);
+
+ if (strcasecmp(section->name, "AUTH") == 0) {
+ location = NULL;
+ title = NULL;
+ users_path = NULL;
+
+ /* Get section keys */
+ location = mk_api->config_section_get_key(section,
+ "Location",
+ MK_RCONF_STR);
+ title = mk_api->config_section_get_key(section,
+ "Title",
+ MK_RCONF_STR);
+
+ users_path = mk_api->config_section_get_key(section,
+ "Users",
+ MK_RCONF_STR);
+
+ /* get or create users file entry */
+ uf = mk_auth_conf_add_users(users_path);
+ if (!uf) {
+ continue;
+ }
+
+ /* Location node */
+ loc = mk_api->mem_alloc(sizeof(struct location));
+ mk_api->pointer_set(&loc->path, location);
+ mk_api->pointer_set(&loc->title, title);
+
+ loc->auth_http_header.data = NULL;
+ mk_api->str_build(&loc->auth_http_header.data,
+ &loc->auth_http_header.len,
+ MK_AUTH_HEADER_TITLE, title);
+
+ loc->users = uf;
+
+ /* Add new location to auth_vhost node */
+ mk_list_add(&loc->_head, &auth_vhost->locations);
+ }
+ }
+
+ /* Link auth_vhost node to global list vhosts_list */
+ mk_list_add(&auth_vhost->_head, &vhosts_list);
+ }
+
+#ifdef TRACE
+ struct mk_list *vh_head, *loc_head;
+ struct vhost *vh_entry;
+ struct location *loc_entry;
+
+ mk_list_foreach(vh_head, &vhosts_list) {
+ vh_entry = mk_list_entry(vh_head, struct vhost, _head);
+ PLUGIN_TRACE("Auth VHost: %p", vh_entry->host);
+
+ mk_list_foreach(loc_head, &vh_entry->locations) {
+ loc_entry = mk_list_entry(loc_head, struct location, _head);
+ PLUGIN_TRACE("---");
+ PLUGIN_TRACE(" location: %s", loc_entry->path);
+ PLUGIN_TRACE(" title : %s", loc_entry->title);
+ PLUGIN_TRACE(" users : %s", loc_entry->users->path);
+ }
+ }
+#endif
+
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/plugins/auth/conf.h b/fluent-bit/lib/monkey/plugins/auth/conf.h
new file mode 100644
index 000000000..90fd8f79a
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/conf.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#ifndef MK_AUTH_CONF_H
+#define MK_AUTH_CONF_H
+
+int mk_auth_conf_init_users_list();
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/auth/sha1.c b/fluent-bit/lib/monkey/plugins/auth/sha1.c
new file mode 100644
index 000000000..53c7946e8
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/sha1.c
@@ -0,0 +1,288 @@
+/*
+ * Code adapted to fill Monkey Project requirements, no big changes
+ * just a few header files added
+ */
+
+#include <arpa/inet.h>
+#include <string.h>
+
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+#include "sha1.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+/*
+ * Force usage of rol or ror by selecting the one with the smaller constant.
+ * It _can_ generate slightly smaller code (a constant of 1 is special), but
+ * perhaps more importantly it's possibly faster on any uarch that does a
+ * rotate with a loop.
+ */
+
+#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
+#define SHA_ROL(x,n) SHA_ASM("rol", x, n)
+#define SHA_ROR(x,n) SHA_ASM("ror", x, n)
+
+#else
+
+#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r)))
+#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n))
+#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n)
+
+#endif
+
+/*
+ * If you have 32 registers or more, the compiler can (and should)
+ * try to change the array[] accesses into registers. However, on
+ * machines with less than ~25 registers, that won't really work,
+ * and at least gcc will make an unholy mess of it.
+ *
+ * So to avoid that mess which just slows things down, we force
+ * the stores to memory to actually happen (we might be better off
+ * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
+ * suggested by Artur Skawina - that will also make gcc unable to
+ * try to do the silly "optimize away loads" part because it won't
+ * see what the value will be).
+ *
+ * Ben Herrenschmidt reports that on PPC, the C version comes close
+ * to the optimized asm with this (ie on PPC you don't want that
+ * 'volatile', since there are lots of registers).
+ *
+ * On ARM we get the best code generation by forcing a full memory barrier
+ * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
+ * the stack frame size simply explode and performance goes down the drain.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+ #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
+#elif defined(__GNUC__) && defined(__arm__)
+ #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
+#else
+ #define setW(x, val) (W(x) = (val))
+#endif
+
+/*
+ * Performance might be improved if the CPU architecture is OK with
+ * unaligned 32-bit loads and a fast ntohl() is available.
+ * Otherwise fall back to byte loads and shifts which is portable,
+ * and is faster on architectures with memory alignment issues.
+ */
+
+#if defined(__i386__) || defined(__x86_64__) || \
+ defined(_M_IX86) || defined(_M_X64) || \
+ defined(__ppc__) || defined(__ppc64__) || \
+ defined(__powerpc__) || defined(__powerpc64__) || \
+ defined(__s390__) || defined(__s390x__)
+
+#define get_be32(p) ntohl(*(unsigned int *)(p))
+#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
+
+#else
+
+#define get_be32(p) ( \
+ (*((unsigned char *)(p) + 0) << 24) | \
+ (*((unsigned char *)(p) + 1) << 16) | \
+ (*((unsigned char *)(p) + 2) << 8) | \
+ (*((unsigned char *)(p) + 3) << 0) )
+#define put_be32(p, v) do { \
+ unsigned int __v = (v); \
+ *((unsigned char *)(p) + 0) = __v >> 24; \
+ *((unsigned char *)(p) + 1) = __v >> 16; \
+ *((unsigned char *)(p) + 2) = __v >> 8; \
+ *((unsigned char *)(p) + 3) = __v >> 0; } while (0)
+
+#endif
+
+/* This "rolls" over the 512-bit array */
+#define W(x) (array[(x)&15])
+
+/*
+ * Where do we get the source from? The first 16 iterations get it from
+ * the input data, the next mix it from the 512-bit array.
+ */
+#define SHA_SRC(t) get_be32(data + t)
+#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
+
+#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
+ unsigned int TEMP = input(t); setW(t, TEMP); \
+ E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
+ B = SHA_ROR(B, 2); } while (0)
+
+#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
+#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
+#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )
+
+static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data)
+{
+ unsigned int A,B,C,D,E;
+ unsigned int array[16];
+
+ A = ctx->H[0];
+ B = ctx->H[1];
+ C = ctx->H[2];
+ D = ctx->H[3];
+ E = ctx->H[4];
+
+ /* Round 1 - iterations 0-16 take their input from 'data' */
+ T_0_15( 0, A, B, C, D, E);
+ T_0_15( 1, E, A, B, C, D);
+ T_0_15( 2, D, E, A, B, C);
+ T_0_15( 3, C, D, E, A, B);
+ T_0_15( 4, B, C, D, E, A);
+ T_0_15( 5, A, B, C, D, E);
+ T_0_15( 6, E, A, B, C, D);
+ T_0_15( 7, D, E, A, B, C);
+ T_0_15( 8, C, D, E, A, B);
+ T_0_15( 9, B, C, D, E, A);
+ T_0_15(10, A, B, C, D, E);
+ T_0_15(11, E, A, B, C, D);
+ T_0_15(12, D, E, A, B, C);
+ T_0_15(13, C, D, E, A, B);
+ T_0_15(14, B, C, D, E, A);
+ T_0_15(15, A, B, C, D, E);
+
+ /* Round 1 - tail. Input from 512-bit mixing array */
+ T_16_19(16, E, A, B, C, D);
+ T_16_19(17, D, E, A, B, C);
+ T_16_19(18, C, D, E, A, B);
+ T_16_19(19, B, C, D, E, A);
+
+ /* Round 2 */
+ T_20_39(20, A, B, C, D, E);
+ T_20_39(21, E, A, B, C, D);
+ T_20_39(22, D, E, A, B, C);
+ T_20_39(23, C, D, E, A, B);
+ T_20_39(24, B, C, D, E, A);
+ T_20_39(25, A, B, C, D, E);
+ T_20_39(26, E, A, B, C, D);
+ T_20_39(27, D, E, A, B, C);
+ T_20_39(28, C, D, E, A, B);
+ T_20_39(29, B, C, D, E, A);
+ T_20_39(30, A, B, C, D, E);
+ T_20_39(31, E, A, B, C, D);
+ T_20_39(32, D, E, A, B, C);
+ T_20_39(33, C, D, E, A, B);
+ T_20_39(34, B, C, D, E, A);
+ T_20_39(35, A, B, C, D, E);
+ T_20_39(36, E, A, B, C, D);
+ T_20_39(37, D, E, A, B, C);
+ T_20_39(38, C, D, E, A, B);
+ T_20_39(39, B, C, D, E, A);
+
+ /* Round 3 */
+ T_40_59(40, A, B, C, D, E);
+ T_40_59(41, E, A, B, C, D);
+ T_40_59(42, D, E, A, B, C);
+ T_40_59(43, C, D, E, A, B);
+ T_40_59(44, B, C, D, E, A);
+ T_40_59(45, A, B, C, D, E);
+ T_40_59(46, E, A, B, C, D);
+ T_40_59(47, D, E, A, B, C);
+ T_40_59(48, C, D, E, A, B);
+ T_40_59(49, B, C, D, E, A);
+ T_40_59(50, A, B, C, D, E);
+ T_40_59(51, E, A, B, C, D);
+ T_40_59(52, D, E, A, B, C);
+ T_40_59(53, C, D, E, A, B);
+ T_40_59(54, B, C, D, E, A);
+ T_40_59(55, A, B, C, D, E);
+ T_40_59(56, E, A, B, C, D);
+ T_40_59(57, D, E, A, B, C);
+ T_40_59(58, C, D, E, A, B);
+ T_40_59(59, B, C, D, E, A);
+
+ /* Round 4 */
+ T_60_79(60, A, B, C, D, E);
+ T_60_79(61, E, A, B, C, D);
+ T_60_79(62, D, E, A, B, C);
+ T_60_79(63, C, D, E, A, B);
+ T_60_79(64, B, C, D, E, A);
+ T_60_79(65, A, B, C, D, E);
+ T_60_79(66, E, A, B, C, D);
+ T_60_79(67, D, E, A, B, C);
+ T_60_79(68, C, D, E, A, B);
+ T_60_79(69, B, C, D, E, A);
+ T_60_79(70, A, B, C, D, E);
+ T_60_79(71, E, A, B, C, D);
+ T_60_79(72, D, E, A, B, C);
+ T_60_79(73, C, D, E, A, B);
+ T_60_79(74, B, C, D, E, A);
+ T_60_79(75, A, B, C, D, E);
+ T_60_79(76, E, A, B, C, D);
+ T_60_79(77, D, E, A, B, C);
+ T_60_79(78, C, D, E, A, B);
+ T_60_79(79, B, C, D, E, A);
+
+ ctx->H[0] += A;
+ ctx->H[1] += B;
+ ctx->H[2] += C;
+ ctx->H[3] += D;
+ ctx->H[4] += E;
+}
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx)
+{
+ ctx->size = 0;
+
+ /* Initialize H with the magic constants (see FIPS180 for constants) */
+ ctx->H[0] = 0x67452301;
+ ctx->H[1] = 0xefcdab89;
+ ctx->H[2] = 0x98badcfe;
+ ctx->H[3] = 0x10325476;
+ ctx->H[4] = 0xc3d2e1f0;
+}
+
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
+{
+ unsigned int lenW = ctx->size & 63;
+
+ ctx->size += len;
+
+ /* Read the data into W and process blocks as they get full */
+ if (lenW) {
+ unsigned int left = 64 - lenW;
+ if (len < left)
+ left = len;
+ memcpy(lenW + (char *)ctx->W, data, left);
+ lenW = (lenW + left) & 63;
+ len -= left;
+ data = ((const char *)data + left);
+ if (lenW)
+ return;
+ blk_SHA1_Block(ctx, ctx->W);
+ }
+ while (len >= 64) {
+ blk_SHA1_Block(ctx, data);
+ data = ((const char *)data + 64);
+ len -= 64;
+ }
+ if (len)
+ memcpy(ctx->W, data, len);
+}
+
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
+{
+ static const unsigned char pad[64] = { 0x80 };
+ unsigned int padlen[2];
+ int i;
+
+ /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
+ padlen[0] = htonl((uint32_t)(ctx->size >> 29));
+ padlen[1] = htonl((uint32_t)(ctx->size << 3));
+
+ i = ctx->size & 63;
+ blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
+ blk_SHA1_Update(ctx, padlen, 8);
+
+ /* Output hash */
+ for (i = 0; i < 5; i++)
+ put_be32(hashout + i*4, ctx->H[i]);
+}
diff --git a/fluent-bit/lib/monkey/plugins/auth/sha1.h b/fluent-bit/lib/monkey/plugins/auth/sha1.h
new file mode 100644
index 000000000..4a75ab351
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/sha1.h
@@ -0,0 +1,22 @@
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+typedef struct {
+ unsigned long long size;
+ unsigned int H[5];
+ unsigned int W[16];
+} blk_SHA_CTX;
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx);
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
+
+#define SHA_CTX blk_SHA_CTX
+#define SHA1_Init blk_SHA1_Init
+#define SHA1_Update blk_SHA1_Update
+#define SHA1_Final blk_SHA1_Final
diff --git a/fluent-bit/lib/monkey/plugins/auth/tools/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/auth/tools/CMakeLists.txt
new file mode 100644
index 000000000..63b445cf7
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/tools/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(src
+ ../sha1.c
+ ../base64.c
+ mk_passwd.c
+ )
+
+include_directories(../)
+add_definitions(-D_FORCE_SYSMALLOC)
+add_executable(mk_passwd ${src})
+
+if(BUILD_LOCAL)
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/")
+else()
+ install(TARGETS mk_passwd RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_SBINDIR})
+endif()
diff --git a/fluent-bit/lib/monkey/plugins/auth/tools/mk_passwd.c b/fluent-bit/lib/monkey/plugins/auth/tools/mk_passwd.c
new file mode 100644
index 000000000..6dba1d3f1
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/auth/tools/mk_passwd.c
@@ -0,0 +1,209 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+#ifdef MALLOC_JEMALLOC
+#undef MALLOC_JEMALLOC
+#endif
+
+#include <monkey/monkey.h>
+#include <monkey/mk_core.h>
+
+#include <getopt.h>
+
+#include "sha1.h"
+#include "base64.h"
+
+#define MAX_LINE_LEN 256
+
+struct mk_passwd_user {
+ char *row;
+ struct mk_list _head;
+};
+
+/* Store a file as a linked list of its lines */
+static struct mk_list passwd_file;
+
+/* Load file to memory from disk
+ * If create_file == MK_TRUE, the file will be rewritten */
+void read_file(char *filename, int create_file)
+{
+ FILE *filein = fopen(filename, "r");
+ char line[MAX_LINE_LEN];
+ struct mk_passwd_user *entry;
+
+ mk_list_init(&passwd_file);
+
+ if (filein == NULL && create_file == MK_FALSE) {
+ printf("Error opening file %s\n", filename);
+ exit(1);
+ }
+
+ if (filein == NULL || create_file == MK_TRUE) {
+ if (filein != NULL)
+ fclose(filein);
+ return;
+ }
+
+ while (fgets(line, MAX_LINE_LEN, filein) != NULL) {
+ entry = malloc(sizeof(*entry));
+ entry->row = strdup(line);
+ mk_list_add(&entry->_head, &passwd_file);
+ }
+ fclose(filein);
+}
+
+/* Store data to disk */
+void dump_file(char *filename)
+{
+ FILE *fileout = fopen(filename, "w");
+ struct mk_list *it, *tmp;
+ struct mk_passwd_user *entry;
+
+ if (!fileout) {
+ printf("Error opening: %s", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ mk_list_foreach_safe(it, tmp, &passwd_file) {
+ entry = mk_list_entry(it, struct mk_passwd_user, _head);
+ fprintf(fileout, "%s", entry->row);
+ mk_list_del(&entry->_head);
+ free(entry->row);
+ free(entry);
+ }
+ fclose(fileout);
+}
+
+/* Return sha1 hash of password
+ * A new line is appended at the hash */
+unsigned char *sha1_hash(const char *password)
+{
+
+ unsigned char sha_hash[20];
+ blk_SHA_CTX sha;
+
+ blk_SHA1_Init(&sha);
+ blk_SHA1_Update(&sha, password, strlen(password));
+ blk_SHA1_Final(sha_hash, &sha);
+
+ return base64_encode(sha_hash, 20, NULL);
+}
+
+void update_user(const char *username, const char *password, int create_user)
+{
+ struct mk_list *it, *tmp;
+ struct mk_passwd_user *entry;
+ unsigned char *hash_passwd;
+ int i;
+
+ mk_list_foreach_safe(it, tmp, &passwd_file) {
+ entry = mk_list_entry(it, struct mk_passwd_user, _head);
+ for (i = 0; entry->row[i] != '\0' && entry->row[i] != ':' && username[i] != '\0' && entry->row[i] == username[i]; i++);
+ if (entry->row[i] != ':' || username[i] != '\0')
+ continue;
+
+ /* Found a match */
+
+ /* Delete user */
+ if (create_user == MK_FALSE) {
+ printf("[-] Deleting user %s\n", username);
+ mk_list_del(&entry->_head);
+ free(entry->row);
+ free(entry);
+ return;
+ }
+
+ /* Update user */
+ printf("[+] Password changed for user %s\n", username);
+ hash_passwd = sha1_hash(password);
+ free(entry->row);
+ entry->row = malloc(512);
+ snprintf(entry->row, 512, "%s:{SHA1}%s", username, hash_passwd);
+ free(hash_passwd);
+
+ return;
+ }
+
+ /* Create user */
+ if (create_user == MK_TRUE) {
+ printf("[+] Adding user %s\n", username);
+ entry = malloc(sizeof(struct mk_passwd_user));
+ entry->row = malloc(512);
+ hash_passwd = sha1_hash(password);
+ snprintf(entry->row, 512, "%s:{SHA1}%s", username, hash_passwd);
+ free(hash_passwd);
+
+ mk_list_add(&entry->_head, &passwd_file);
+ }
+}
+
+static void print_help(int full_help)
+{
+ printf("Usage: mk_passwd [-c] [-D] filename username password\n");
+ if (full_help == MK_TRUE) {
+ printf("\nOptions:\n");
+ printf(" -h, --help\tshow this help message and exit\n");
+ printf(" -c\t\tCreate a new mkpasswd file, overwriting any existing file.\n");
+ printf(" -D\t\tRemove the given user from the password file.\n");
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ int create_user = MK_TRUE;
+ int create_file = MK_FALSE;
+ int show_help = MK_FALSE;
+ char *filename = NULL;
+ char *username = NULL;
+ char *password = NULL;
+
+ /* Command line options */
+ static const struct option long_opts[] = {
+ {"create", no_argument, NULL, 'c'},
+ {"delete_user", no_argument, NULL, 'D'},
+ {"help", no_argument, NULL, 'h'},
+ };
+
+ /* Parse options */
+ while ((opt = getopt_long(argc, argv, "hbDc", long_opts, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ create_file = MK_TRUE;
+ break;
+ case 'D':
+ create_user = MK_FALSE;
+ break;
+ case 'h':
+ show_help = MK_TRUE;
+ break;
+ }
+ }
+
+ /* Retrieve filename, username and password */
+ while (optind < argc) {
+ if (filename == NULL)
+ filename = argv[optind++];
+ else if (username == NULL)
+ username = argv[optind++];
+ else if (password == NULL)
+ password = argv[optind++];
+ }
+
+ if (show_help == MK_TRUE) {
+ print_help(MK_TRUE);
+ exit(0);
+ }
+
+ /* If delete_user option is provided, do not provide a password */
+ if ((password != NULL) ^ (create_user == MK_TRUE)) {
+ print_help(MK_FALSE);
+ exit(1);
+ }
+
+ /* Process request */
+ read_file(filename, create_file);
+ update_user(username, password, create_user);
+ dump_file(filename);
+
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/plugins/cgi/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/cgi/CMakeLists.txt
new file mode 100644
index 000000000..121144aa1
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(src
+ cgi.c
+ event.c
+ request.c
+ )
+
+MONKEY_PLUGIN(cgi "${src}")
diff --git a/fluent-bit/lib/monkey/plugins/cgi/cgi.c b/fluent-bit/lib/monkey/plugins/cgi/cgi.c
new file mode 100644
index 000000000..58c7a647f
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/cgi.c
@@ -0,0 +1,501 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright (C) 2012-2013, Lauri Kasanen
+ *
+ * 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 <monkey/mk_stream.h>
+#include "cgi.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+
+void cgi_finish(struct cgi_request *r)
+{
+ /*
+ * Unregister & close the CGI child process pipe reader fd from the
+ * thread event loop, otherwise we may get unexpected notifications.
+ */
+ mk_api->ev_del(mk_api->sched_loop(), (struct mk_event *) r);
+ close(r->fd);
+ if (r->chunked && r->active == MK_TRUE) {
+ PLUGIN_TRACE("CGI sending Chunked EOF");
+ channel_write(r, "0\r\n\r\n", 5);
+ }
+
+ /* Try to kill any child process */
+ if (r->child > 0) {
+ kill(r->child, SIGKILL);
+ r->child = 0;
+ }
+
+ /* Invalidte our socket handler */
+ requests_by_socket[r->socket] = NULL;
+ if (r->active == MK_TRUE) {
+ mk_api->http_request_end(r->plugin, r->cs, r->hangup);
+ }
+ cgi_req_del(r);
+}
+
+int swrite(const int fd, const void *buf, const size_t count)
+{
+ ssize_t pos = count, ret = 0;
+
+ while (pos > 0 && ret >= 0) {
+ ret = write(fd, buf, pos);
+ if (ret < 0) {
+ return ret;
+ }
+
+ pos -= ret;
+ buf += ret;
+ }
+ return count;
+}
+
+int channel_write(struct cgi_request *r, void *buf, size_t count)
+{
+ int ret;
+
+ if (r->active == MK_FALSE) {
+ return -1;
+ }
+
+ MK_TRACE("channel write: %d bytes", count);
+ mk_stream_in_raw(&r->sr->stream,
+ NULL,
+ buf, count,
+ NULL, NULL);
+
+ ret = mk_api->channel_flush(r->sr->session->channel);
+ if (ret & MK_CHANNEL_ERROR) {
+ r->active = MK_FALSE;
+ cgi_finish(r);
+ }
+ return 0;
+}
+
+static void cgi_write_post(void *p)
+{
+ const struct post_t * const in = p;
+
+ swrite(in->fd, in->buf, in->len);
+ close(in->fd);
+}
+
+static int do_cgi(const char *const __restrict__ file,
+ const char *const __restrict__ url,
+ struct mk_http_request *sr,
+ struct mk_http_session *cs,
+ struct mk_plugin *plugin,
+ char *interpreter,
+ char *mimetype)
+{
+ int ret;
+ int devnull;
+ const int socket = cs->socket;
+ struct file_info finfo;
+ struct cgi_request *r = NULL;
+ struct mk_event *event;
+ char *env[30];
+ int writepipe[2], readpipe[2];
+ (void) plugin;
+
+ /* Unchanging env vars */
+ env[0] = "PATH_INFO=";
+ env[1] = "GATEWAY_INTERFACE=CGI/1.1";
+ env[2] = "REDIRECT_STATUS=200";
+ const int env_start = 3;
+ char *protocol;
+ unsigned long len;
+
+ /* Dynamic env vars */
+ unsigned short envpos = env_start;
+
+ char method[SHORTLEN];
+ char *query = NULL;
+ char request_uri[PATHLEN];
+ char script_filename[PATHLEN];
+ char script_name[PATHLEN];
+ char query_string[PATHLEN];
+ char remote_addr[INET6_ADDRSTRLEN+SHORTLEN];
+ char tmpaddr[INET6_ADDRSTRLEN], *ptr = tmpaddr;
+ char remote_port[SHORTLEN];
+ char content_length[SHORTLEN];
+ char content_type[SHORTLEN];
+ char server_software[SHORTLEN];
+ char server_protocol[SHORTLEN];
+ char http_host[SHORTLEN];
+
+ /* Check the interpreter exists */
+ if (interpreter) {
+ ret = mk_api->file_get_info(interpreter, &finfo, MK_FILE_EXEC);
+ if (ret == -1 ||
+ (finfo.is_file == MK_FALSE && finfo.is_link == MK_FALSE) ||
+ finfo.exec_access == MK_FALSE) {
+ return 500;
+ }
+ }
+
+ if (mimetype) {
+ sr->content_type.data = mimetype;
+ sr->content_type.len = strlen(mimetype);
+ }
+
+ snprintf(method, SHORTLEN, "REQUEST_METHOD=%.*s", (int) sr->method_p.len, sr->method_p.data);
+ env[envpos++] = method;
+
+ snprintf(server_software, SHORTLEN, "SERVER_SOFTWARE=%s",
+ mk_api->config->server_signature);
+ env[envpos++] = server_software;
+
+ snprintf(http_host, SHORTLEN, "HTTP_HOST=%.*s", (int) sr->host.len, sr->host.data);
+ env[envpos++] = http_host;
+
+ if (sr->protocol == MK_HTTP_PROTOCOL_11)
+ protocol = MK_HTTP_PROTOCOL_11_STR;
+ else
+ protocol = MK_HTTP_PROTOCOL_10_STR;
+
+ snprintf(server_protocol, SHORTLEN, "SERVER_PROTOCOL=%s", protocol);
+ env[envpos++] = server_protocol;
+
+ if (sr->query_string.len) {
+ query = mk_api->mem_alloc_z(sr->query_string.len + 1);
+ memcpy(query, sr->query_string.data, sr->query_string.len);
+ snprintf(request_uri, PATHLEN, "REQUEST_URI=%s?%s", url, query);
+ }
+ else {
+ snprintf(request_uri, PATHLEN, "REQUEST_URI=%s", url);
+ }
+ env[envpos++] = request_uri;
+
+ snprintf(script_filename, PATHLEN, "SCRIPT_FILENAME=%s", file);
+ env[envpos++] = script_filename;
+
+ snprintf(script_name, PATHLEN, "SCRIPT_NAME=%s", url);
+ env[envpos++] = script_name;
+
+ if (query) {
+ snprintf(query_string, PATHLEN, "QUERY_STRING=%s", query);
+ env[envpos++] = query_string;
+ mk_api->mem_free(query);
+ }
+
+ if (mk_api->socket_ip_str(socket, &ptr, INET6_ADDRSTRLEN, &len) < 0)
+ tmpaddr[0] = '\0';
+ snprintf(remote_addr, INET6_ADDRSTRLEN+SHORTLEN, "REMOTE_ADDR=%s", tmpaddr);
+ env[envpos++] = remote_addr;
+
+ snprintf(remote_port, SHORTLEN, "REMOTE_PORT=%ld", sr->port);
+ env[envpos++] = remote_port;
+
+ if (sr->data.len) {
+ snprintf(content_length, SHORTLEN, "CONTENT_LENGTH=%lu", sr->data.len);
+ env[envpos++] = content_length;
+ }
+
+ if (sr->content_type.len) {
+ snprintf(content_type, SHORTLEN, "CONTENT_TYPE=%.*s", (int)sr->content_type.len, sr->content_type.data);
+ env[envpos++] = content_type;
+ }
+
+
+ /* Must be NULL-terminated */
+ env[envpos] = NULL;
+
+ /* pipes, from monkey's POV */
+ if (pipe(writepipe) || pipe(readpipe)) {
+ mk_err("Failed to create pipe");
+ return 403;
+ }
+
+ pid_t pid = vfork();
+ if (pid < 0) {
+ mk_err("Failed to fork");
+ return 403;
+ }
+
+ /* Child */
+ if (pid == 0) {
+ close(writepipe[1]);
+ close(readpipe[0]);
+
+ /* Our stdin is the read end of monkey's writing */
+ if (dup2(writepipe[0], 0) < 0) {
+ mk_err("dup2 failed");
+ _exit(1);
+ }
+ close(writepipe[0]);
+
+ /* Our stdout is the write end of monkey's reading */
+ if (dup2(readpipe[1], 1) < 0) {
+ mk_err("dup2 failed");
+ _exit(1);
+ }
+ close(readpipe[1]);
+
+ /* Our stderr goes to /dev/null */
+ devnull = open("/dev/null", O_WRONLY);
+ if (devnull == -1) {
+ perror("open");
+ _exit(1);
+ }
+
+ if (dup2(devnull, 2) < 0) {
+ mk_err("dup2 failed");
+ _exit(1);
+ }
+ close(devnull);
+
+ char *argv[3] = { NULL };
+
+ char *tmp = mk_api->str_dup(file);
+ if (chdir(dirname(tmp)))
+ _exit(1);
+
+ char *tmp2 = mk_api->str_dup(file);
+ argv[0] = basename(tmp2);
+
+ /* Restore signals for the child */
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+
+ if (!interpreter) {
+ execve(file, argv, env);
+ }
+ else {
+ argv[0] = basename(interpreter);
+ argv[1] = (char *) file;
+ execve(interpreter, argv, env);
+ }
+ /* Exec failed, return */
+ _exit(1);
+ }
+
+ /* Yay me */
+ close(writepipe[0]);
+ close(readpipe[1]);
+
+ /* If we have POST data to write, spawn a thread to do that */
+ if (sr->data.len) {
+ struct post_t p;
+ pthread_t tid;
+
+ p.fd = writepipe[1];
+ p.buf = sr->data.data;
+ p.len = sr->data.len;
+
+ ret = mk_api->worker_spawn(cgi_write_post, &p, &tid);
+ if (ret != 0) {
+ return 403;
+ }
+ }
+ else {
+ close(writepipe[1]);
+ }
+
+ r = cgi_req_create(readpipe[0], socket, plugin, sr, cs);
+ if (!r) {
+ return 403;
+ }
+ r->child = pid;
+
+ /*
+ * Hang up?: by default Monkey assumes the CGI scripts generate
+ * content dynamically (no Content-Length header), so for such HTTP/1.0
+ * clients we should close the connection as KeepAlive is not supported
+ * by specification, only on HTTP/1.1 where the Chunked Transfer encoding
+ * exists.
+ */
+ if (r->sr->protocol >= MK_HTTP_PROTOCOL_11) {
+ r->hangup = MK_FALSE;
+ }
+
+ /* Set transfer encoding */
+ if (r->sr->protocol >= MK_HTTP_PROTOCOL_11 &&
+ (r->sr->headers.status < MK_REDIR_MULTIPLE ||
+ r->sr->headers.status > MK_REDIR_USE_PROXY)) {
+ r->sr->headers.transfer_encoding = MK_HEADER_TE_TYPE_CHUNKED;
+ r->chunked = 1;
+ }
+
+ /* Register the 'request' context */
+ cgi_req_add(r);
+
+ /* Prepare the built-in event structure */
+ event = &r->event;
+ event->fd = readpipe[0];
+ event->type = MK_EVENT_CUSTOM;
+ event->mask = MK_EVENT_EMPTY;
+ event->data = r;
+ event->handler = cb_cgi_read;
+
+ /* Register the event into the worker event-loop */
+ ret = mk_api->ev_add(mk_api->sched_loop(),
+ readpipe[0],
+ MK_EVENT_CUSTOM, MK_EVENT_READ, r);
+ if (ret != 0) {
+ return 403;
+ }
+
+
+ /* XXX Fixme: this needs to be atomic */
+ requests_by_socket[socket] = r;
+ return 200;
+}
+
+int mk_cgi_plugin_init(struct plugin_api **api, char *confdir)
+{
+ struct rlimit lim;
+ (void) confdir;
+
+ mk_api = *api;
+ mk_list_init(&cgi_global_matches);
+ pthread_key_create(&cgi_request_list, NULL);
+
+ /*
+ * We try to perform some quick lookup over the list of CGI
+ * instances. We do this with a fixed length array, if you use CGI
+ * you don't care too much about performance anyways.
+ */
+ getrlimit(RLIMIT_NOFILE, &lim);
+ requests_by_socket = mk_api->mem_alloc_z(sizeof(struct cgi_request *) * lim.rlim_cur);
+
+ /* Make sure we act good if the child dies */
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGCHLD, SIG_IGN);
+
+ return 0;
+}
+
+int mk_cgi_plugin_exit()
+{
+ regfree(&match_regex);
+ mk_api->mem_free(requests_by_socket);
+
+ return 0;
+}
+
+int mk_cgi_stage30(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ int n_params,
+ struct mk_list *params)
+{
+ char *interpreter = NULL;
+ char *mimetype = NULL;
+ struct mk_vhost_handler_param *param;
+ (void) plugin;
+
+ const char *const file = sr->real_path.data;
+
+ if (!sr->file_info.is_file) {
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+
+ /* start running the CGI */
+ if (cgi_req_get(cs->socket)) {
+ PLUGIN_TRACE("Error, someone tried to retry\n");
+ return MK_PLUGIN_RET_CONTINUE;
+ }
+
+ if (n_params > 0) {
+ /* Interpreter */
+ param = mk_api->handler_param_get(0, params);
+ if (param) {
+ interpreter = param->p.data;
+ }
+
+ /* Mimetype */
+ param = mk_api->handler_param_get(0, params);
+ if (param) {
+ mimetype = param->p.data;
+ }
+ }
+
+ int status = do_cgi(file, sr->uri_processed.data,
+ sr, cs, plugin, interpreter, mimetype);
+
+ /* These are just for the other plugins, such as logger; bogus data */
+ mk_api->header_set_http_status(sr, status);
+ if (status != 200) {
+ return MK_PLUGIN_RET_CLOSE_CONX;
+ }
+
+ sr->headers.cgi = SH_CGI;
+ return MK_PLUGIN_RET_CONTINUE;
+}
+
+/*
+ * Invoked everytime a remote client drop the active connection, this
+ * callback is triggered by the Monkey Scheduler
+ */
+int mk_cgi_stage30_hangup(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr)
+{
+ struct cgi_request *r;
+ (void) sr;
+ (void) plugin;
+
+ PLUGIN_TRACE("CGI / Parent connection closed (hangup)");
+ r = requests_by_socket[cs->socket];
+ if (!r) {
+ return -1;
+ }
+
+ r->active = MK_FALSE;
+ cgi_finish(r);
+ return 0;
+}
+
+void mk_cgi_worker_init()
+{
+ struct mk_list *list = mk_api->mem_alloc_z(sizeof(struct mk_list));
+
+ mk_list_init(list);
+ pthread_setspecific(cgi_request_list, (void *) list);
+}
+
+
+struct mk_plugin_stage mk_plugin_stage_cgi = {
+ .stage30 = &mk_cgi_stage30,
+ .stage30_hangup = &mk_cgi_stage30_hangup
+};
+
+struct mk_plugin mk_plugin_cgi = {
+ /* Identification */
+ .shortname = "cgi",
+ .name = "Common Gateway Interface",
+ .version = MK_VERSION_STR,
+ .hooks = MK_PLUGIN_STAGE,
+
+ /* Init / Exit */
+ .init_plugin = mk_cgi_plugin_init,
+ .exit_plugin = mk_cgi_plugin_exit,
+
+ /* Init Levels */
+ .master_init = NULL,
+ .worker_init = mk_cgi_worker_init,
+
+ /* Type */
+ .stage = &mk_plugin_stage_cgi
+};
diff --git a/fluent-bit/lib/monkey/plugins/cgi/cgi.h b/fluent-bit/lib/monkey/plugins/cgi/cgi.h
new file mode 100644
index 000000000..27123b7ae
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/cgi.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright (C) 2012-2013, Lauri Kasanen
+ *
+ * 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.
+ */
+
+#ifndef MK_CGI_H
+#define MK_CGI_H
+
+#include <monkey/mk_api.h>
+
+#include <sys/types.h>
+#include <regex.h>
+#include <signal.h>
+#include <libgen.h>
+
+enum {
+ BUFLEN = 4096,
+ PATHLEN = 1024,
+ SHORTLEN = 64
+};
+
+regex_t match_regex;
+
+struct cgi_request **requests_by_socket;
+
+struct post_t {
+ int fd;
+ void *buf;
+ unsigned long len;
+};
+
+struct cgi_match_t {
+ regex_t match;
+ char *bin;
+ mk_ptr_t content_type;
+
+ struct mk_list _head;
+};
+
+struct cgi_vhost_t {
+ struct mk_vhost *host;
+ struct mk_list matches;
+};
+
+struct cgi_vhost_t *cgi_vhosts;
+struct mk_list cgi_global_matches;
+
+
+struct cgi_request {
+ /* Built-in reference for the event loop */
+ struct mk_event event;
+
+ char in_buf[BUFLEN];
+
+ struct mk_list _head;
+
+ struct mk_plugin *plugin;
+ struct mk_http_request *sr;
+ struct mk_http_session *cs;
+
+ unsigned int in_len;
+
+ int fd; /* Pipe the CGI proc */
+ int socket; /* Client connection */
+ int hangup; /* Should close connection when done ? */
+ int active; /* Active session ? */
+ pid_t child; /* child process ID */
+ unsigned char status_done;
+ unsigned char all_headers_done;
+ unsigned char chunked;
+};
+
+/* Global list per worker */
+pthread_key_t cgi_request_list;
+
+extern struct cgi_request **requests_by_socket;
+
+void cgi_finish(struct cgi_request *r);
+
+int swrite(const int fd, const void *buf, const size_t count);
+int channel_write(struct cgi_request *r, void *buf, size_t count);
+
+struct cgi_request *cgi_req_create(int fd, int socket,
+ struct mk_plugin *plugin,
+ struct mk_http_request *sr,
+ struct mk_http_session *cs);
+void cgi_req_add(struct cgi_request *r);
+int cgi_req_del(struct cgi_request *r);
+
+// Get the CGI request by the client socket
+static inline struct cgi_request *cgi_req_get(int socket)
+{
+ struct cgi_request *r = requests_by_socket[socket];
+ return r;
+}
+
+// Get the CGI request by the CGI app's fd
+static inline struct cgi_request *cgi_req_get_by_fd(int fd)
+{
+ struct mk_list *list, *node;
+ struct cgi_request *r;
+
+ list = pthread_getspecific(cgi_request_list);
+ if (mk_list_is_empty(list) == 0)
+ return NULL;
+
+ mk_list_foreach(node, list) {
+ r = mk_list_entry(node, struct cgi_request, _head);
+ if (r->fd == fd)
+ return r;
+ }
+
+ return NULL;
+}
+
+int cb_cgi_read(void *data);
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/cgi/event.c b/fluent-bit/lib/monkey/plugins/cgi/event.c
new file mode 100644
index 000000000..b8d395260
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/event.c
@@ -0,0 +1,170 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright (C) 2012-2013, Lauri Kasanen
+ *
+ * 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 "cgi.h"
+
+/*
+ * The reason for this function is that some CGI apps
+ *
+ * use LFLF and some use CRLFCRLF.
+ *
+ * If that app then sends content that has the other break
+ * in the beginning, monkey can accidentally send part of the
+ * content as headers.
+ */
+
+static char *getearliestbreak(const char buf[], const unsigned bufsize,
+ unsigned char * const advance)
+ {
+ char * const crend = memmem(buf, bufsize, MK_IOV_CRLFCRLF,
+ sizeof(MK_IOV_CRLFCRLF) - 1);
+ char * const lfend = memmem(buf, bufsize, MK_IOV_LFLF,
+ sizeof(MK_IOV_LFLF) - 1);
+
+ if (!crend && !lfend)
+ return NULL;
+
+ /* If only one found, return that one */
+ if (!crend) {
+ *advance = 2;
+ return lfend;
+ }
+ if (!lfend)
+ return crend;
+
+ /* Both found, return the earlier one - the latter one is part of content */
+ if (lfend < crend) {
+ *advance = 2;
+ return lfend;
+ }
+ return crend;
+}
+
+int process_cgi_data(struct cgi_request *r)
+{
+ int ret;
+ int len;
+ int status;
+ char *buf = r->in_buf;
+ char *outptr = r->in_buf;
+ char *end;
+ char *endl;
+ unsigned char advance;
+
+ mk_api->socket_cork_flag(r->cs->socket, TCP_CORK_OFF);
+ if (!r->status_done && r->in_len >= 8) {
+ if (memcmp(buf, "Status: ", 8) == 0) {
+ status = atoi(buf + 8);
+ mk_api->header_set_http_status(r->sr, status);
+ endl = memchr(buf + 8, '\n', r->in_len - 8);
+ if (!endl) {
+ return MK_PLUGIN_RET_EVENT_OWNED;
+ }
+ else {
+ endl++;
+ outptr = endl;
+ r->in_len -= endl - buf;
+ }
+ }
+ else if (memcmp(buf, "HTTP", 4) == 0) {
+ status = atoi(buf + 9);
+ mk_api->header_set_http_status(r->sr, status);
+
+ endl = memchr(buf + 8, '\n', r->in_len - 8);
+ if (!endl) {
+ return MK_PLUGIN_RET_EVENT_OWNED;
+ }
+ else {
+ endl++;
+ outptr = endl;
+ r->in_len -= endl - buf;
+ }
+ }
+ mk_api->header_prepare(r->plugin, r->cs, r->sr);
+ r->status_done = 1;
+ }
+
+ if (!r->all_headers_done) {
+ advance = 4;
+
+ /* Write the rest of the headers without chunking */
+ end = getearliestbreak(outptr, r->in_len, &advance);
+ if (!end) {
+ /* Let's return until we have the headers break */
+ return MK_PLUGIN_RET_EVENT_OWNED;
+ }
+ end += advance;
+ len = end - outptr;
+ channel_write(r, outptr, len);
+ outptr += len;
+ r->in_len -= len;
+
+ r->all_headers_done = 1;
+ if (r->in_len == 0) {
+ return MK_PLUGIN_RET_EVENT_OWNED;
+ }
+ }
+
+ if (r->chunked) {
+ char tmp[16];
+ len = snprintf(tmp, 16, "%x\r\n", r->in_len);
+ ret = channel_write(r, tmp, len);
+ if (ret < 0)
+ return MK_PLUGIN_RET_EVENT_CLOSE;
+ }
+
+ ret = channel_write(r, outptr, r->in_len);
+ if (ret < 0) {
+ return MK_PLUGIN_RET_EVENT_CLOSE;
+ }
+
+ r->in_len = 0;
+ if (r->chunked) {
+ channel_write(r, MK_CRLF, 2);
+ }
+ return MK_PLUGIN_RET_EVENT_OWNED;
+}
+
+int cb_cgi_read(void *data)
+{
+ int n;
+ struct cgi_request *r = data;
+
+ if (r->active == MK_FALSE) {
+ return -1;
+ }
+
+ if ((BUFLEN - r->in_len) < 1) {
+ PLUGIN_TRACE("CLOSE BY SIZE");
+ cgi_finish(r);
+ return -1;
+ }
+
+ n = read(r->fd, r->in_buf + r->in_len, BUFLEN - r->in_len);
+ PLUGIN_TRACE("FD=%i CGI READ=%d", r->fd, n);
+ if (n <= 0) {
+ /* It most of cases this means the child process finished */
+ cgi_finish(r);
+ return MK_PLUGIN_RET_EVENT_CLOSE;
+ }
+ r->in_len += n;
+ process_cgi_data(r);
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/plugins/cgi/request.c b/fluent-bit/lib/monkey/plugins/cgi/request.c
new file mode 100644
index 000000000..c51a13831
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cgi/request.c
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright (C) 2012-2013, Lauri Kasanen
+ *
+ * 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 "cgi.h"
+
+struct cgi_request *cgi_req_create(int fd, int socket,
+ struct mk_plugin *plugin,
+ struct mk_http_request *sr,
+ struct mk_http_session *cs)
+{
+ struct cgi_request *cgi;
+
+ cgi = mk_api->mem_alloc_z(sizeof(struct cgi_request));
+ if (!cgi) {
+ return NULL;
+ }
+
+ cgi->fd = fd;
+ cgi->socket = socket;
+ cgi->plugin = plugin;
+ cgi->sr = sr;
+ cgi->cs = cs;
+ cgi->hangup = MK_TRUE;
+ cgi->active = MK_TRUE;
+ cgi->in_len = 0;
+
+ cgi->event.mask = MK_EVENT_EMPTY;
+ cgi->event.status = MK_EVENT_NONE;
+
+ return cgi;
+}
+
+void cgi_req_add(struct cgi_request *r)
+{
+ struct mk_list *list;
+
+ list = pthread_getspecific(cgi_request_list);
+ mk_list_add(&r->_head, list);
+}
+
+int cgi_req_del(struct cgi_request *r)
+{
+ PLUGIN_TRACE("Delete request child_fd=%i child_pid=%lu",
+ r->fd, r->child);
+
+ mk_list_del(&r->_head);
+ if (r->active == MK_FALSE) {
+ mk_api->sched_event_free(&r->event);
+ }
+ else {
+ mk_mem_free(r);
+ }
+
+ return 0;
+}
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/ABOUT b/fluent-bit/lib/monkey/plugins/cheetah/ABOUT
new file mode 100644
index 000000000..229bf62c2
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/ABOUT
@@ -0,0 +1,4 @@
+Cheetah! Plugin
+===============
+This plugin provides a command line interface for Monkey,
+it works like a shell.
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/cheetah/CMakeLists.txt
new file mode 100644
index 000000000..ca760c337
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(src
+ cheetah.c
+ loop.c
+ cmd.c
+ cutils.c
+ )
+
+MONKEY_PLUGIN(cheetah "${src}")
+add_subdirectory(conf)
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/OPTIONAL b/fluent-bit/lib/monkey/plugins/cheetah/OPTIONAL
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/OPTIONAL
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/cheetah.c b/fluent-bit/lib/monkey/plugins/cheetah/cheetah.c
new file mode 100644
index 000000000..489e73422
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/cheetah.c
@@ -0,0 +1,162 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+/* Monkey Plugin Interface */
+#include <monkey/mk_api.h>
+
+/* Local header files */
+#include "cmd.h"
+#include "cutils.h"
+#include "cheetah.h"
+#include "loop.h"
+
+void mk_cheetah_welcome_msg()
+{
+ CHEETAH_WRITE("\n%s%s***%s Welcome to %sCheetah!%s, the %sMonkey Shell %s:) %s***%s\n",
+ ANSI_BOLD, ANSI_YELLOW,
+ ANSI_WHITE, ANSI_GREEN,
+ ANSI_WHITE, ANSI_RED, ANSI_WHITE, ANSI_YELLOW, ANSI_RESET);
+ CHEETAH_WRITE("\n << %sType 'help' or '\\h' for help%s >>\n\n",
+ ANSI_BLUE, ANSI_RESET);
+ CHEETAH_FLUSH();
+}
+
+static int mk_cheetah_config(char *path)
+{
+ unsigned long len;
+ char *listen = NULL;
+ char *default_file = NULL;
+ struct mk_rconf *conf;
+ struct mk_rconf_section *section;
+
+ /* this variable is defined in cheetah.h and points to
+ * the FILE *descriptor where to write out the data
+ */
+ cheetah_output = NULL;
+
+ /* read configuration file */
+ mk_api->str_build(&default_file, &len, "%scheetah.conf", path);
+ conf = mk_api->config_open(default_file);
+ if (!conf) {
+ return -1;
+ }
+
+ section = mk_api->config_section_get(conf, "CHEETAH");
+
+ if (!section) {
+ CHEETAH_WRITE("\nError, could not find CHEETAH tag");
+ return -1;
+ }
+
+ /* no longer needed */
+ mk_api->mem_free(default_file);
+
+ /* Listen directive */
+ listen = mk_api->config_section_get_key(section, "Listen", MK_RCONF_STR);
+
+ if (strcasecmp(listen, LISTEN_STDIN_STR) == 0) {
+ listen_mode = LISTEN_STDIN;
+ }
+ else if (strcasecmp(listen, LISTEN_SERVER_STR) == 0) {
+ listen_mode = LISTEN_SERVER;
+ }
+ else {
+ printf("\nCheetah! Error: Invalid LISTEN value");
+ return -1;
+ }
+
+ /* Cheetah cannot work in STDIN mode if Monkey is working in background */
+ if (listen_mode == LISTEN_STDIN && mk_api->config->is_daemon == MK_TRUE) {
+ printf("\nCheetah!: Forcing SERVER mode as Monkey is running in background\n");
+ fflush(stdout);
+ listen_mode = LISTEN_SERVER;
+ }
+
+ return 0;
+}
+
+static void mk_cheetah_init(void *args)
+{
+ struct mk_server *server = args;
+
+ /* Rename worker */
+ mk_api->worker_rename("monkey: cheetah");
+
+ /* Open right FDs for I/O */
+ if (listen_mode == LISTEN_STDIN) {
+ cheetah_input = stdin;
+ cheetah_output = stdout;
+ mk_cheetah_loop_stdin(server);
+ }
+ else if (listen_mode == LISTEN_SERVER) {
+ mk_cheetah_loop_server(server);
+ }
+}
+
+/* This function is called when the plugin is loaded, it must
+ * return
+ */
+int mk_cheetah_plugin_init(struct plugin_api **api, char *confdir)
+{
+ int ret;
+ mk_api = *api;
+ init_time = time(NULL);
+
+ ret = mk_cheetah_config(confdir);
+ return ret;
+}
+
+int mk_cheetah_plugin_exit()
+{
+ if (listen_mode == LISTEN_SERVER) {
+ /* Remote named pipe */
+ unlink(cheetah_server);
+ mk_api->mem_free(cheetah_server);
+ }
+
+ return 0;
+}
+
+int mk_cheetah_master_init(struct mk_server *server)
+{
+ int ret;
+ pthread_t tid;
+
+ ret = mk_api->worker_spawn(mk_cheetah_init, server, &tid);
+ if (ret != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+struct mk_plugin mk_plugin_cheetah = {
+ /* Identification */
+ .shortname = "cheetah",
+ .name = "Cheetah! Shell",
+ .version = MK_VERSION_STR,
+
+ /* Init / Exit */
+ .init_plugin = mk_cheetah_plugin_init,
+ .exit_plugin = mk_cheetah_plugin_exit,
+
+ /* Init Levels */
+ .master_init = mk_cheetah_master_init,
+ .worker_init = NULL
+};
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/cheetah.h b/fluent-bit/lib/monkey/plugins/cheetah/cheetah.h
new file mode 100644
index 000000000..5bf6c14e5
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/cheetah.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#ifndef MK_CHEETAH_H
+#define MK_CHEETAH_H
+
+/* Commands */
+#define MK_CHEETAH_CLEAR "clear"
+#define MK_CHEETAH_CLEAR_SC "\\c"
+
+#define MK_CHEETAH_CONFIG "config"
+#define MK_CHEETAH_CONFIG_SC "\\f"
+
+#define MK_CHEETAH_STATUS "status"
+#define MK_CHEETAH_STATUS_SC "\\s"
+
+#define MK_CHEETAH_HELP "help"
+#define MK_CHEETAH_HELP_SC "\\h"
+
+#define MK_CHEETAH_SHELP "?"
+#define MK_CHEETAH_SHELP_SC "\\?"
+
+#define MK_CHEETAH_UPTIME "uptime"
+#define MK_CHEETAH_UPTIME_SC "\\u"
+
+#define MK_CHEETAH_PLUGINS "plugins"
+#define MK_CHEETAH_PLUGINS_SC "\\g"
+
+#define MK_CHEETAH_VHOSTS "vhosts"
+#define MK_CHEETAH_VHOSTS_SC "\\v"
+
+#define MK_CHEETAH_WORKERS "workers"
+#define MK_CHEETAH_WORKERS_SC "\\w"
+
+#define MK_CHEETAH_QUIT "quit"
+#define MK_CHEETAH_QUIT_SC "\\q"
+
+/* Constants */
+#define MK_CHEETAH_PROMPT "%s%scheetah>%s "
+#define MK_CHEETAH_PROC_TASK "/proc/%i/task/%i/stat"
+#define MK_CHEETAH_ONEDAY 86400
+#define MK_CHEETAH_ONEHOUR 3600
+#define MK_CHEETAH_ONEMINUTE 60
+
+/* Configurarion: Listen */
+#define LISTEN_STDIN_STR "STDIN"
+#define LISTEN_SERVER_STR "SERVER"
+
+#define LISTEN_STDIN 0
+#define LISTEN_SERVER 1
+
+int listen_mode;
+
+char *cheetah_server;
+
+int cheetah_socket;
+FILE *cheetah_input;
+FILE *cheetah_output;
+
+/* functions */
+void mk_cheetah_welcome_msg();
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/cmd.c b/fluent-bit/lib/monkey/plugins/cheetah/cmd.c
new file mode 100644
index 000000000..6f02dad23
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/cmd.c
@@ -0,0 +1,472 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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 <monkey/mk_api.h>
+
+#include <pwd.h>
+#include <ctype.h>
+
+#include "cheetah.h"
+#include "cutils.h"
+#include "cmd.h"
+
+/* strip leading and trailing space from input command line. */
+static char *strip_whitespace(char *cmd)
+{
+ char *end;
+ while (isspace(*cmd))
+ cmd++;
+ if (*cmd == 0)
+ return cmd;
+ end = cmd + strlen(cmd) - 1;
+ while (end > cmd && isspace(*end))
+ end--;
+ end++;
+ *end = '\0';
+ return cmd;
+}
+
+int mk_cheetah_cmd(char *raw_cmd, struct mk_server *server)
+{
+ char *cmd = strip_whitespace(raw_cmd);
+ if (strcmp(cmd, MK_CHEETAH_CONFIG) == 0 ||
+ strcmp(cmd, MK_CHEETAH_CONFIG_SC) == 0) {
+ mk_cheetah_cmd_config(server);
+ }
+ else if (strcmp(cmd, MK_CHEETAH_STATUS) == 0 ||
+ strcmp(cmd, MK_CHEETAH_STATUS_SC) == 0) {
+ mk_cheetah_cmd_status(server);
+ }
+ else if (strcmp(cmd, MK_CHEETAH_CLEAR) == 0 ||
+ strcmp(cmd, MK_CHEETAH_CLEAR_SC) == 0) {
+ mk_cheetah_cmd_clear();
+ }
+ else if (strcmp(cmd, MK_CHEETAH_UPTIME) == 0 ||
+ strcmp(cmd, MK_CHEETAH_UPTIME_SC) == 0) {
+ mk_cheetah_cmd_uptime(server);
+ }
+ else if (strcmp(cmd, MK_CHEETAH_PLUGINS) == 0 ||
+ strcmp(cmd, MK_CHEETAH_PLUGINS_SC) == 0) {
+ mk_cheetah_cmd_plugins(server);
+ }
+ else if (strcmp(cmd, MK_CHEETAH_WORKERS) == 0 ||
+ strcmp(cmd, MK_CHEETAH_WORKERS_SC) == 0) {
+ mk_cheetah_cmd_workers(server);
+ }
+ else if (strcmp(cmd, MK_CHEETAH_VHOSTS) == 0 ||
+ strcmp(cmd, MK_CHEETAH_VHOSTS_SC) == 0) {
+ mk_cheetah_cmd_vhosts(server);
+ }
+ else if (strcmp(cmd, MK_CHEETAH_HELP) == 0 ||
+ strcmp(cmd, MK_CHEETAH_HELP_SC) == 0 ||
+ strcmp(cmd, MK_CHEETAH_SHELP) == 0 ||
+ strcmp(cmd, MK_CHEETAH_SHELP_SC) == 0) {
+ mk_cheetah_cmd_help();
+ }
+ else if (strcmp(cmd, MK_CHEETAH_QUIT) == 0 ||
+ strcmp(cmd, MK_CHEETAH_QUIT_SC) == 0) {
+ return mk_cheetah_cmd_quit();
+ }
+ else if (strlen(cmd) == 0) {
+ return 0;
+ }
+ else {
+ CHEETAH_WRITE("Invalid command, type 'help' for a list of available commands\n");
+ }
+
+ CHEETAH_FLUSH();
+ return 0;
+}
+
+void mk_cheetah_cmd_clear()
+{
+ CHEETAH_WRITE("\033[2J\033[1;1H");
+}
+
+void mk_cheetah_cmd_uptime(struct mk_server *server)
+{
+ int days;
+ int hours;
+ int minutes;
+ int seconds;
+ long int upmind;
+ long int upminh;
+ long int uptime;
+ (void) server;
+
+ /* uptime in seconds */
+ uptime = time(NULL) - init_time;
+
+ /* days */
+ days = uptime / MK_CHEETAH_ONEDAY;
+ upmind = uptime - (days * MK_CHEETAH_ONEDAY);
+
+ /* hours */
+ hours = upmind / MK_CHEETAH_ONEHOUR;
+ upminh = upmind - hours * MK_CHEETAH_ONEHOUR;
+
+ /* minutes */
+ minutes = upminh / MK_CHEETAH_ONEMINUTE;
+ seconds = upminh - minutes * MK_CHEETAH_ONEMINUTE;
+
+ CHEETAH_WRITE
+ ("Server has been running: %i day%s, %i hour%s, %i minute%s and %i second%s\n\n",
+ days, (days > 1) ? "s" : "", hours, (hours > 1) ? "s" : "", minutes,
+ (minutes > 1) ? "s" : "", seconds, (seconds > 1) ? "s" : "");
+}
+
+void mk_cheetah_cmd_plugins_print_core(struct mk_list *list)
+{
+ struct mk_plugin *p;
+ struct mk_list *head;
+
+ CHEETAH_WRITE("\n%s[MASTER INIT]%s", ANSI_BOLD ANSI_BLUE, ANSI_RESET);
+
+ mk_list_foreach(head, list) {
+ p = mk_list_entry(head, struct mk_plugin, _head);
+
+ if (p->master_init) {
+ CHEETAH_WRITE("\n [%s] %s v%s on \"%s\"",
+ p->shortname, p->name, p->version, p->path);
+ }
+ }
+
+ CHEETAH_WRITE("\n\n%s[WORKER INIT]%s", ANSI_BOLD ANSI_BLUE, ANSI_RESET);
+
+ mk_list_foreach(head, list) {
+ p = mk_list_entry(head, struct mk_plugin, _head);
+
+ if (p->worker_init) {
+ CHEETAH_WRITE("\n [%s] %s v%s on \"%s\"",
+ p->shortname, p->name, p->version, p->path);
+ }
+ }
+
+ CHEETAH_WRITE("\n\n");
+}
+
+void mk_cheetah_cmd_plugins_print_network(struct mk_list *list)
+{
+ struct mk_plugin *p;
+ struct mk_list *head;
+
+ CHEETAH_WRITE("%s[NETWORK I/O]%s", ANSI_BOLD ANSI_RED, ANSI_RESET);
+
+ mk_list_foreach(head, list) {
+ p = mk_list_entry(head, struct mk_plugin, _head);
+ if (p->hooks & MK_PLUGIN_NETWORK_LAYER) {
+ CHEETAH_WRITE("\n [%s] %s v%s on \"%s\"",
+ p->shortname, p->name, p->version, p->path);
+ }
+ }
+
+ CHEETAH_WRITE("\n");
+}
+
+void mk_cheetah_cmd_plugins(struct mk_server *server)
+{
+ struct mk_plugin *p;
+ struct mk_plugin_stage *s;
+ struct mk_list *head;
+
+ if (mk_list_is_empty(&server->stage10_handler)) {
+ CHEETAH_WRITE("%s[%sSTAGE_10%s]%s",
+ ANSI_BOLD, ANSI_YELLOW, ANSI_WHITE, ANSI_RESET);
+ mk_list_foreach(head, &mk_api->config->stage10_handler) {
+ s = mk_list_entry(head, struct mk_plugin_stage, _head);
+ p = s->plugin;
+ CHEETAH_WRITE("\n [%s] %s v%s on \"%s\"",
+ p->shortname, p->name, p->version, p->path);
+ }
+ }
+
+ if (mk_list_is_empty(&mk_api->config->stage20_handler)) {
+ CHEETAH_WRITE("%s[%sSTAGE_20%s]%s",
+ ANSI_BOLD, ANSI_YELLOW, ANSI_WHITE, ANSI_RESET);
+ mk_list_foreach(head, &mk_api->config->stage20_handler) {
+ s = mk_list_entry(head, struct mk_plugin_stage, _head);
+ p = s->plugin;
+ CHEETAH_WRITE("\n [%s] %s v%s on \"%s\"",
+ p->shortname, p->name, p->version, p->path);
+ }
+ }
+
+ if (mk_list_is_empty(&mk_api->config->stage30_handler)) {
+ CHEETAH_WRITE("%s[%sSTAGE_30%s]%s",
+ ANSI_BOLD, ANSI_YELLOW, ANSI_WHITE, ANSI_RESET);
+ mk_list_foreach(head, &mk_api->config->stage30_handler) {
+ s = mk_list_entry(head, struct mk_plugin_stage, _head);
+ p = s->plugin;
+ CHEETAH_WRITE("\n [%s] %s v%s on \"%s\"",
+ p->shortname, p->name, p->version, p->path);
+ }
+ }
+
+ if (mk_list_is_empty(&mk_api->config->stage40_handler)) {
+ CHEETAH_WRITE("%s[%sSTAGE_40%s]%s",
+ ANSI_BOLD, ANSI_YELLOW, ANSI_WHITE, ANSI_RESET);
+ mk_list_foreach(head, &mk_api->config->stage40_handler) {
+ s = mk_list_entry(head, struct mk_plugin_stage, _head);
+ p = s->plugin;
+ CHEETAH_WRITE("\n [%s] %s v%s on \"%s\"",
+ p->shortname, p->name, p->version, p->path);
+ }
+ }
+
+ if (mk_list_is_empty(&mk_api->config->stage50_handler)) {
+ CHEETAH_WRITE("%s[%sSTAGE_50%s]%s",
+ ANSI_BOLD, ANSI_YELLOW, ANSI_WHITE, ANSI_RESET);
+ mk_list_foreach(head, &mk_api->config->stage50_handler) {
+ s = mk_list_entry(head, struct mk_plugin_stage, _head);
+ p = s->plugin;
+ CHEETAH_WRITE("\n [%s] %s v%s on \"%s\"",
+ p->shortname, p->name, p->version, p->path);
+ }
+ }
+
+ CHEETAH_WRITE("\n\n");
+}
+
+void mk_cheetah_cmd_vhosts(struct mk_server *server)
+{
+ struct mk_vhost *entry_host;
+ struct mk_vhost_alias *entry_alias;
+ struct mk_rconf_section *section;
+ struct mk_rconf_entry *entry;
+ struct mk_list *hosts = &server->hosts;
+ struct mk_list *aliases;
+ struct mk_list *head_host;
+ struct mk_list *head_alias;
+ struct mk_list *head_sections;
+ struct mk_list *head_entries;
+
+ mk_list_foreach(head_host, hosts) {
+ entry_host = mk_list_entry(head_host, struct mk_vhost, _head);
+
+ aliases = &entry_host->server_names;
+ entry_alias = mk_list_entry_first(aliases, struct mk_vhost_alias, _head);
+ CHEETAH_WRITE("%s[%sVHost '%s'%s%s]%s\n",
+ ANSI_BOLD, ANSI_YELLOW,
+ entry_alias->name, ANSI_BOLD, ANSI_WHITE, ANSI_RESET);
+
+ CHEETAH_WRITE(" - Names : ");
+ mk_list_foreach(head_alias, aliases) {
+ entry_alias = mk_list_entry(head_alias, struct mk_vhost_alias, _head);
+ CHEETAH_WRITE("%s ", entry_alias->name);
+ }
+ CHEETAH_WRITE("\n");
+
+ CHEETAH_WRITE(" - Document root : %s\n", entry_host->documentroot.data);
+ CHEETAH_WRITE(" - Config file : %s\n", entry_host->file);
+
+ if (!entry_host->config) {
+ continue;
+ }
+
+ mk_list_foreach(head_sections, &entry_host->config->sections) {
+ section = mk_list_entry(head_sections, struct mk_rconf_section, _head);
+ CHEETAH_WRITE(" %s+%s [%s]\n", ANSI_GREEN, ANSI_RESET,
+ section->name);
+
+ mk_list_foreach(head_entries, &section->entries) {
+ entry = mk_list_entry(head_entries, struct mk_rconf_entry, _head);
+ CHEETAH_WRITE(" - %11.10s : %s\n", entry->key, entry->val);
+ }
+ }
+ }
+
+ CHEETAH_WRITE("\n");
+}
+
+void mk_cheetah_cmd_workers(struct mk_server *server)
+{
+ int i;
+ unsigned long long active_connections;
+ struct mk_sched_worker *node;
+ struct mk_sched_ctx *ctx;
+
+ ctx = server->sched_ctx;
+ node = ctx->workers;
+ for (i=0; i < server->workers; i++) {
+ active_connections = (node[i].accepted_connections - node[i].closed_connections);
+
+ CHEETAH_WRITE("* Worker %i\n", node[i].idx);
+ CHEETAH_WRITE(" - Task ID : %i\n", node[i].pid);
+ CHEETAH_WRITE(" - Active Connections: %llu\n", active_connections);
+ }
+
+ CHEETAH_WRITE("\n");
+}
+
+int mk_cheetah_cmd_quit()
+{
+ CHEETAH_WRITE("Cheeta says: Good Bye!\n");
+ if (listen_mode == LISTEN_STDIN) {
+ pthread_exit(NULL);
+ return 0;
+ }
+ else {
+ return -1;
+ }
+}
+
+void mk_cheetah_cmd_help()
+{
+ CHEETAH_WRITE("List of available commands for Cheetah Shell\n");
+ CHEETAH_WRITE("\ncommand shortcut description");
+ CHEETAH_WRITE("\n----------------------------------------------------");
+ CHEETAH_WRITE("\n? (\\?) Synonym for 'help'");
+ CHEETAH_WRITE("\nconfig (\\f) Display global configuration");
+ CHEETAH_WRITE("\nplugins (\\g) List loaded plugins and associated stages");
+ CHEETAH_WRITE("\nstatus (\\s) Display general web server information");
+ CHEETAH_WRITE("\nuptime (\\u) Display how long the web server has been running");
+ CHEETAH_WRITE("\nvhosts (\\v) List virtual hosts configured");
+ CHEETAH_WRITE("\nworkers (\\w) Show thread workers information\n");
+ CHEETAH_WRITE("\nclear (\\c) Clear screen");
+ CHEETAH_WRITE("\nhelp (\\h) Print this help");
+ CHEETAH_WRITE("\nquit (\\q) Exit Cheetah shell :_(\n\n");
+}
+
+static void mk_cheetah_listen_config(struct mk_server *server)
+{
+ struct mk_list *head;
+ struct mk_config_listener *listener;
+
+ mk_list_foreach(head, &server->listeners) {
+ listener = mk_list_entry(head, struct mk_config_listener, _head);
+ CHEETAH_WRITE("\nListen on : %s:%s",
+ listener->address,
+ listener->port);
+ }
+}
+
+void mk_cheetah_cmd_config(struct mk_server *server)
+{
+ struct mk_string_line *entry;
+ struct mk_list *head;
+ struct mk_config_listener *listener;
+
+ listener = mk_list_entry_first(&server->listeners,
+ struct mk_config_listener,
+ _head);
+
+ CHEETAH_WRITE("Basic configuration");
+ CHEETAH_WRITE("\n-------------------");
+ mk_cheetah_listen_config(server);
+ CHEETAH_WRITE("\nWorkers : %i threads", mk_api->config->workers);
+ CHEETAH_WRITE("\nTimeout : %i seconds", mk_api->config->timeout);
+ CHEETAH_WRITE("\nPidFile : %s.%s",
+ mk_api->config->path_conf_pidfile,
+ listener->port);
+ CHEETAH_WRITE("\nUserDir : %s",
+ mk_api->config->conf_user_pub);
+
+
+ if (mk_list_is_empty(mk_api->config->index_files) == 0) {
+ CHEETAH_WRITE("\nIndexFile : No index files defined");
+ }
+ else {
+ CHEETAH_WRITE("\nIndexFile : ");
+ mk_list_foreach(head, mk_api->config->index_files) {
+ entry = mk_list_entry(head, struct mk_string_line, _head);
+ CHEETAH_WRITE("%s ", entry->val);
+ }
+
+ }
+
+ CHEETAH_WRITE("\nHideVersion : ");
+ if (mk_api->config->hideversion == MK_TRUE) {
+ CHEETAH_WRITE("On");
+ }
+ else {
+ CHEETAH_WRITE("Off");
+ }
+
+ CHEETAH_WRITE("\nResume : ");
+ if (mk_api->config->resume == MK_TRUE) {
+ CHEETAH_WRITE("On");
+ }
+ else {
+ CHEETAH_WRITE("Off");
+ }
+
+ CHEETAH_WRITE("\nUser : %s", mk_api->config->user);
+ CHEETAH_WRITE("\n\nAdvanced configuration");
+ CHEETAH_WRITE("\n----------------------");
+ CHEETAH_WRITE("\nKeepAlive : ");
+ if (mk_api->config->keep_alive == MK_TRUE) {
+ CHEETAH_WRITE("On");
+ }
+ else {
+ CHEETAH_WRITE("Off");
+ }
+ CHEETAH_WRITE("\nMaxKeepAliveRequest : %i req/connection",
+ mk_api->config->max_keep_alive_request);
+ CHEETAH_WRITE("\nKeepAliveTimeout : %i seconds", mk_api->config->keep_alive_timeout);
+ CHEETAH_WRITE("\nMaxRequestSize : %i KB",
+ mk_api->config->max_request_size/1024);
+ CHEETAH_WRITE("\nSymLink : ");
+ if (mk_api->config->symlink == MK_TRUE) {
+ CHEETAH_WRITE("On");
+ }
+ else {
+ CHEETAH_WRITE("Off");
+ }
+ CHEETAH_WRITE("\n\n");
+}
+
+void mk_cheetah_cmd_status(struct mk_server *server)
+{
+ int nthreads = server->workers;
+ char tmp[64];
+
+ CHEETAH_WRITE("Monkey Version : %s\n", MK_VERSION_STR);
+ CHEETAH_WRITE("Configuration path : %s\n", server->path_conf_root);
+
+ CHEETAH_WRITE("Cheetah! mode : ");
+ if (listen_mode == LISTEN_STDIN) {
+ CHEETAH_WRITE("STDIN\n");
+ }
+ else {
+ CHEETAH_WRITE("SERVER @ %s\n", cheetah_server);
+ }
+
+ CHEETAH_WRITE("Process ID : %i\n", getpid());
+ CHEETAH_WRITE("Process User : ");
+ mk_cheetah_print_running_user();
+ mk_cheetah_listen_config(server);
+
+ CHEETAH_WRITE("\n");
+ CHEETAH_WRITE("Worker Threads : %i (per configuration: %i)\n",
+ nthreads, server->workers);
+
+ CHEETAH_WRITE("Memory Allocator : ");
+#ifdef MALLOC_LIBC
+ CHEETAH_WRITE("libc, system default\n");
+#else
+ CHEETAH_WRITE("Jemalloc\n");
+#endif
+
+ if (mk_api->kernel_features_print(tmp, sizeof(tmp), server) > 0) {
+ CHEETAH_WRITE("Kernel Features : %s\n", tmp);
+ }
+
+ CHEETAH_WRITE("Events backend : %s\n", mk_api->ev_backend());
+ CHEETAH_WRITE("\n");
+}
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/cmd.h b/fluent-bit/lib/monkey/plugins/cheetah/cmd.h
new file mode 100644
index 000000000..617a88be1
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/cmd.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+time_t init_time;
+
+/* commands */
+int mk_cheetah_cmd(char *cmd, struct mk_server *server);
+
+void mk_cheetah_cmd_clear();
+void mk_cheetah_cmd_uptime(struct mk_server *server);
+
+/* Plugins commands */
+void mk_cheetah_cmd_plugins_print_stage(struct mk_list *list, const char *stage,
+ int stage_bw);
+void mk_cheetah_cmd_plugins_print_core(struct mk_list *list);
+void mk_cheetah_cmd_plugins_print_network(struct mk_list *list);
+void mk_cheetah_cmd_plugins(struct mk_server *server);
+
+void mk_cheetah_cmd_vhosts(struct mk_server *server);
+void mk_cheetah_cmd_workers(struct mk_server *server);
+
+int mk_cheetah_cmd_quit();
+void mk_cheetah_cmd_help();
+void mk_cheetah_cmd_config(struct mk_server *server);
+void mk_cheetah_cmd_status(struct mk_server *server);
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/conf/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/cheetah/conf/CMakeLists.txt
new file mode 100644
index 000000000..b7224f351
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/conf/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(conf_dir "${MK_PATH_CONF}/plugins/cheetah/")
+
+install(DIRECTORY DESTINATION ${conf_dir})
+
+if(BUILD_LOCAL)
+ file(COPY cheetah.conf DESTINATION ${conf_dir})
+else()
+ install(FILES cheetah.conf DESTINATION ${conf_dir})
+endif()
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/conf/cheetah.conf b/fluent-bit/lib/monkey/plugins/cheetah/conf/cheetah.conf
new file mode 100644
index 000000000..d9448926e
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/conf/cheetah.conf
@@ -0,0 +1,17 @@
+# Cheetah! configuration
+# ======================
+# Cheetah! is the Monkey Sheel and in this file you can define
+# the basic setup and behavior expected.
+#
+
+[CHEETAH]
+ # Listen :
+ # --------
+ # Cheetah! listen for input commands as any shell, this can be done
+ # using the standard keyboard input or through a unix pipe where you
+ # need to connect using the Cheetah! client.
+ #
+ # The Listen directive allows you to define which input method to use.
+ # Valid values for Listen are STDIN or SERVER.
+ #
+ Listen SERVER
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/cutils.c b/fluent-bit/lib/monkey/plugins/cheetah/cutils.c
new file mode 100644
index 000000000..3c0bd825f
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/cutils.c
@@ -0,0 +1,117 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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 <monkey/mk_api.h>
+
+#include <pwd.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "cheetah.h"
+#include "cutils.h"
+
+void mk_cheetah_print_worker_memory_usage(pid_t pid)
+{
+ int s = 1024;
+ char *buf;
+ pid_t ppid;
+ FILE *f;
+
+ ppid = getpid();
+ buf = mk_api->mem_alloc(s);
+ sprintf(buf, MK_CHEETAH_PROC_TASK, ppid, pid);
+
+ f = fopen(buf, "r");
+ if (!f) {
+ CHEETAH_WRITE("Cannot get details\n");
+ return;
+ }
+
+ buf = fgets(buf, s, f);
+ fclose(f);
+ if (!buf) {
+ CHEETAH_WRITE("Cannot format details\n");
+ return;
+ }
+
+ CHEETAH_WRITE("\n");
+ return;
+
+/*
+ int n, c;
+ int init = 0;
+ int last = 0;
+ char *value;
+
+ while ((n = mk_api->str_search(buf + last, " ", MK_STR_SENSITIVE)) > 0) {
+ if (c == 23) {
+ value = mk_api->str_copy_substr(buf, init, last + n);
+ printf("%s\n", value);
+ mk_mem_free(buf);
+ mk_mem_free(value);
+ return;
+ }
+ init = last + n + 1;
+ last += n + 1;
+ c++;
+ }*/
+}
+
+void mk_cheetah_print_running_user()
+{
+ struct passwd pwd;
+ struct passwd *result;
+ char *buf;
+ long bufsize;
+ uid_t uid;
+
+ bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (bufsize == -1) {
+ bufsize = 16384;
+ }
+
+ buf = mk_api->mem_alloc_z(bufsize);
+ uid = getuid();
+ getpwuid_r(uid, &pwd, buf, bufsize, &result);
+
+ CHEETAH_WRITE("%s", pwd.pw_name);
+ mk_api->mem_free(buf);
+}
+
+int mk_cheetah_write(const char *format, ...)
+{
+ int len = 0;
+ char buf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ len = vsprintf(buf, format, ap);
+
+ if (listen_mode == LISTEN_STDIN) {
+ len = fprintf(cheetah_output, buf, NULL);
+ }
+ else if (listen_mode == LISTEN_SERVER) {
+ len = write(cheetah_socket, buf, len);
+ }
+
+ memset(buf, '\0', sizeof(buf));
+ va_end(ap);
+
+ return len;
+}
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/cutils.h b/fluent-bit/lib/monkey/plugins/cheetah/cutils.h
new file mode 100644
index 000000000..c6948a55a
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/cutils.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#ifndef MK_CHEETAH_CUTILS_H
+#define MK_CHEETAH_CUTILS_H
+
+#include <stdio.h>
+
+#define CHEETAH_WRITE(...) mk_cheetah_write(__VA_ARGS__);
+#define CHEETAH_FLUSH() fflush(cheetah_output);fflush(cheetah_input);
+
+void mk_cheetah_print_worker_memory_usage(pid_t pid);
+void mk_cheetah_print_running_user();
+int mk_cheetah_write(const char *format, ...);
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/loop.c b/fluent-bit/lib/monkey/plugins/cheetah/loop.c
new file mode 100644
index 000000000..e30f26f2c
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/loop.c
@@ -0,0 +1,149 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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 <monkey/mk_api.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/* Monkey Plugin Interface */
+#include "cheetah.h"
+#include "cutils.h"
+#include "cmd.h"
+#include "loop.h"
+
+void mk_cheetah_loop_stdin(struct mk_server *server)
+{
+ int len;
+ char cmd[200];
+ char line[200];
+ char *rcmd;
+
+ mk_cheetah_welcome_msg();
+
+ while (1) {
+ CHEETAH_WRITE(MK_CHEETAH_PROMPT, ANSI_BOLD, ANSI_GREEN, ANSI_RESET);
+
+ rcmd = fgets(line, sizeof(line), cheetah_input);
+ if (!rcmd) {
+ continue;
+ }
+
+ len = strlen(line);
+
+ if (len == 0){
+ CHEETAH_WRITE("\n");
+ mk_cheetah_cmd_quit();
+ }
+
+ strncpy(cmd, line, len - 1);
+ cmd[len - 1] = '\0';
+
+ mk_cheetah_cmd(cmd, server);
+ memset(line, '\0', sizeof(line));
+ }
+}
+
+void mk_cheetah_loop_server(struct mk_server *server)
+{
+ int n, ret;
+ int buf_len;
+ unsigned long len;
+ char buf[1024];
+ char cmd[1024];
+ int server_fd;
+ int remote_fd;
+ size_t address_length;
+ struct sockaddr_un address;
+ socklen_t socket_size = sizeof(struct sockaddr_in);
+ struct mk_config_listener *listener;
+
+ /* Create listening socket */
+ server_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (server_fd < 0) {
+ perror("socket() failed");
+ exit(EXIT_FAILURE);
+ }
+
+ listener = mk_list_entry_first(&mk_api->config->listeners,
+ struct mk_config_listener,
+ _head);
+ cheetah_server = NULL;
+ mk_api->str_build(&cheetah_server, &len, "/tmp/cheetah.%s",
+ listener->port);
+ unlink(cheetah_server);
+
+ address.sun_family = AF_UNIX;
+ sprintf(address.sun_path, "%s", cheetah_server);
+ address_length = sizeof(address.sun_family) + len + 1;
+
+ if (bind(server_fd, (struct sockaddr *) &address, address_length) != 0) {
+ perror("bind");
+ mk_err("Cheetah: could not bind address %s", address.sun_path);
+ exit(EXIT_FAILURE);
+ }
+
+ if (listen(server_fd, 5) != 0) {
+ perror("listen");
+ exit(EXIT_FAILURE);
+ }
+
+ while (1) {
+ /* Listen for incoming connections */
+ remote_fd = accept(server_fd, (struct sockaddr *) &address, &socket_size);
+ cheetah_socket = remote_fd;
+
+ buf_len = 0;
+ memset(buf, '\0', 1024);
+
+ /* Send welcome message and prompt */
+ mk_cheetah_welcome_msg();
+ CHEETAH_WRITE(MK_CHEETAH_PROMPT, ANSI_BOLD, ANSI_GREEN, ANSI_RESET);
+
+ while (1) {
+ /* Read incoming data */
+ n = read(remote_fd, buf+buf_len, 1024 - buf_len);
+ if (n <= 0) {
+ break;
+ }
+ else {
+ buf_len += n;
+ if (buf[buf_len-1] == '\n') {
+ /* Filter command */
+ strncpy(cmd, buf, buf_len - 1);
+ cmd[buf_len - 1] = '\0';
+
+ /* Run command */
+ ret = mk_cheetah_cmd(cmd, server);
+
+ if (ret == -1) {
+ break;
+ }
+
+ /* Write prompt */
+ CHEETAH_WRITE(MK_CHEETAH_PROMPT, ANSI_BOLD, ANSI_GREEN, ANSI_RESET);
+ buf_len = 0;
+ memset(buf, '\0', 1024);
+ }
+ }
+ }
+
+ close(remote_fd);
+ }
+}
diff --git a/fluent-bit/lib/monkey/plugins/cheetah/loop.h b/fluent-bit/lib/monkey/plugins/cheetah/loop.h
new file mode 100644
index 000000000..232a00b61
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/cheetah/loop.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#ifndef MK_CHEETAH_LOOP_H
+#define MK_CHEETAH_LOOP_H
+
+void mk_cheetah_loop_stdin();
+void mk_cheetah_loop_server();
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/ABOUT b/fluent-bit/lib/monkey/plugins/dirlisting/ABOUT
new file mode 100644
index 000000000..2d71cb77e
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/ABOUT
@@ -0,0 +1,4 @@
+Directory Listing Plugin
+========================
+When a directory is requested, this plugin will show
+an HTML list of the available content to the client.
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/dirlisting/CMakeLists.txt
new file mode 100644
index 000000000..7432e1091
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(src
+ dirlisting.c
+ )
+
+MONKEY_PLUGIN(dirlisting "${src}")
+
+add_subdirectory(conf) \ No newline at end of file
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/OPTIONAL b/fluent-bit/lib/monkey/plugins/dirlisting/OPTIONAL
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/OPTIONAL
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/conf/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/dirlisting/conf/CMakeLists.txt
new file mode 100644
index 000000000..4faf80a4d
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/conf/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(conf_dir "${MK_PATH_CONF}/plugins/dirlisting/")
+
+install(DIRECTORY DESTINATION ${conf_dir})
+
+if(BUILD_LOCAL)
+ file(COPY dirhtml.conf DESTINATION ${conf_dir})
+ file(COPY themes DESTINATION ${conf_dir})
+else()
+ install(FILES dirhtml.conf DESTINATION ${conf_dir})
+ install(DIRECTORY themes DESTINATION ${conf_dir})
+endif() \ No newline at end of file
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/conf/dirhtml.conf b/fluent-bit/lib/monkey/plugins/dirlisting/conf/dirhtml.conf
new file mode 100644
index 000000000..c73188df2
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/conf/dirhtml.conf
@@ -0,0 +1,2 @@
+[DIRLISTING]
+ Theme bootstrap
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/entry.theme b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/entry.theme
new file mode 100644
index 000000000..8c1ef026d
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/entry.theme
@@ -0,0 +1,5 @@
+<tr>
+ <td><a title='%_target_title_%' href='%_target_url_%'>%_target_name_%</a></td>
+ <td>%_target_time_%</td>
+ <td>%_target_size_%</td>
+</tr>
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/footer.theme b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/footer.theme
new file mode 100644
index 000000000..88290ac8b
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/footer.theme
@@ -0,0 +1,8 @@
+ <tbody>
+</table>
+</div>
+</div>
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
+<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
+</body>
+</HTML>
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/header.theme b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/header.theme
new file mode 100644
index 000000000..f7f6cfcfd
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/bootstrap/header.theme
@@ -0,0 +1,27 @@
+<html>
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Index of %_html_title_%</title>
+ <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+ <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
+ <link href="http://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">
+ </head>
+ <body>
+ <div class="container">
+ <h1>Index of %_html_title_%</h1>
+ <div class="table-responsive">
+ <table class="table table-hover table-bordered">
+ <thead>
+ <tr>
+ <th>
+ Name
+ </th>
+ <th>
+ Last time modified
+ </th>
+ <th>
+ Size
+ </th>
+ </tr>
+ </thead>
+ <tbody>
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/entry.theme b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/entry.theme
new file mode 100644
index 000000000..af00e250c
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/entry.theme
@@ -0,0 +1,14 @@
+<TR>
+<TD class="row">
+ <A title='%_target_title_%' href='%_target_url_%'>
+ %_target_name_%
+ </A>
+</TD>
+<TD class="row">
+ %_target_time_%
+</TD>
+<TD class="row">
+ %_target_size_%
+ %_theme_path_%
+</TD>
+</TR>
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/footer.theme b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/footer.theme
new file mode 100644
index 000000000..c785ba97f
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/footer.theme
@@ -0,0 +1,7 @@
+</TABLE>
+
+</TD>
+</TR>
+</TABLE>
+</BODY></HTML>
+
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/header.theme b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/header.theme
new file mode 100644
index 000000000..6a11a458f
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/conf/themes/guineo/header.theme
@@ -0,0 +1,58 @@
+<HTML>
+ <HEAD>
+ <META http-equiv="content-type" content="text/html; charset=UTF-8">
+ <STYLE type="text/css">
+ H1.header {
+ text-align: center;
+ color: #4a77a0;
+ font-size: 12pt;
+ font-weight: bold;
+ }
+ .row {
+ color: #6c95bc;
+ font-size: 10pt;
+ background-color: #ffffff;
+ padding-left: 0.4em;
+ }
+ .title {color: #617691; font-size: 10pt; font-weight: bold;}
+
+ a:link {
+ color: #475f7e;
+ }
+ a:visited {
+ color: #ac7a2a;
+ }
+ a:hover {
+ color: #475f7e;
+ background-color: #dbdbdb;
+ text-decoration: none;
+ }
+ a:active {
+ color: #333333;
+ }
+ </STYLE>
+
+ <TITLE>Index of %_html_title_%</TITLE>
+ </HEAD>
+<BODY>
+
+<CENTER>
+<IMG src="/imgs/monkey_logo.png">
+<BR>
+<h1 class="header">Index of %_html_title_%</h1>
+<TABLE cellpadding="0" cellspacing="0" border="0" bgcolor="#000000" width="50%">
+<TR>
+<TD>
+
+<TABLE cellpadding="2" cellspacing="1" border="0" bgcolor="#e7e7e7" width="100%">
+<TR bgcolor="#ececec">
+ <TD class="title">
+ Name
+ </TD>
+ <TD class="title">
+ Last time modified
+ </TD>
+ <TD class="title">
+ Size
+ </TD>
+</TR>
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/dirlisting.c b/fluent-bit/lib/monkey/plugins/dirlisting/dirlisting.c
new file mode 100644
index 000000000..1ffd3fac4
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/dirlisting.c
@@ -0,0 +1,949 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+/*
+ * Some history about this plugin
+ * ------------------------------
+ * 2008 - Rewrite module, suport dynamic themes by Eduardo Silva
+ * 2008 - Felipe Astroza (max) provided the mk_dirhtml_human_readable_size_func()
+ * 2007 - Add struct client_request support by Eduardo
+ * 2002 - Original version written by Daniel R. Ome
+ */
+
+#include <monkey/mk_api.h>
+#include <monkey/mk_stream.h>
+#include "dirlisting.h"
+
+#include <time.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+const mk_ptr_t mk_dirhtml_default_mime = mk_ptr_init(MK_DIRHTML_DEFAULT_MIME);
+const mk_ptr_t mk_dir_iov_dash = mk_ptr_init("-");
+const mk_ptr_t mk_dir_iov_none = mk_ptr_init("");
+const mk_ptr_t mk_dir_iov_slash = mk_ptr_init("/");
+
+void mk_dirhtml_cb_body_rows(struct mk_stream_input *in);
+
+/* Function wrote by Max (Felipe Astroza), thanks! */
+static char *mk_dirhtml_human_readable_size(char *buf, size_t size, int len)
+{
+ unsigned long u = 1024, i;
+ static const char *__units[] = {
+ "b", "K", "M", "G",
+ "T", "P", "E", "Z", "Y", NULL
+ };
+
+ for (i = 0; __units[i] != NULL; i++) {
+ if ((size / u) == 0) {
+ break;
+ }
+ u *= 1024;
+ }
+ if (!i) {
+ snprintf(buf, size, "%lu%s", (long unsigned int) len, __units[0]);
+ }
+ else {
+ float fsize = (float) ((double) len / (u / 1024));
+ snprintf(buf, size, "%.1f%s", fsize, __units[i]);
+ }
+
+ return buf;
+}
+
+static struct mk_f_list *mk_dirhtml_create_element(char *file,
+ unsigned char type,
+ char *full_path,
+ unsigned long *list_len)
+{
+ int n;
+ struct tm *st_time;
+ struct mk_f_list *entry;
+
+ entry = mk_api->mem_alloc_z(sizeof(struct mk_f_list));
+
+ if (mk_api->file_get_info(full_path, &entry->info, MK_FILE_READ) != 0) {
+ mk_api->mem_free(entry);
+ return NULL;
+ }
+
+ strcpy(entry->name, file);
+ entry->type = type;
+
+ st_time = localtime((time_t *) & entry->info.last_modification);
+ n = strftime(entry->ft_modif, MK_DIRHTML_FMOD_LEN, "%d-%b-%G %H:%M", st_time);
+ if (n == 0) {
+ mk_mem_free(entry);
+ return NULL;
+ }
+
+ if (type != DT_DIR) {
+ mk_dirhtml_human_readable_size(entry->size,
+ sizeof(entry->size),
+ entry->info.size);
+ }
+ else {
+ entry->size[0] = '-';
+ entry->size[1] = '\0';
+ }
+
+ *list_len = *list_len + 1;
+
+ return entry;
+}
+
+static struct mk_list *mk_dirhtml_create_list(DIR * dir, char *path,
+ unsigned long *list_len)
+{
+ char full_path[PATH_MAX];
+ struct mk_list *list;
+ struct dirent *ent;
+ struct mk_f_list *entry = 0;
+
+ list = mk_api->mem_alloc(sizeof(struct mk_list));
+ mk_list_init(list);
+
+ while ((ent = readdir(dir)) != NULL) {
+ if ((ent->d_name[0] == '.') && (strcmp(ent->d_name, "..") != 0))
+ continue;
+
+ /* Look just for files and dirs */
+ if (ent->d_type != DT_REG && ent->d_type != DT_DIR
+ && ent->d_type != DT_LNK && ent->d_type != DT_UNKNOWN) {
+ continue;
+ }
+
+ snprintf(full_path, PATH_MAX, "%s%s", path, ent->d_name);
+ entry = mk_dirhtml_create_element(ent->d_name,
+ ent->d_type, full_path, list_len);
+ if (!entry) {
+ continue;
+ }
+
+ mk_list_add(&entry->_head, list);
+ }
+
+ return list;
+}
+
+/* Read dirhtml config and themes */
+int mk_dirhtml_conf(char *confdir)
+{
+ int ret = 0;
+ unsigned long len;
+ char *conf_file = NULL;
+
+ mk_api->str_build(&conf_file, &len, "%s", confdir);
+
+ /* Read configuration */
+ ret = mk_dirhtml_read_config(conf_file);
+ if (ret < 0) {
+ mk_mem_free(conf_file);
+ return -1;
+ }
+
+ /*
+ * This function will load the default theme setted in dirhtml_conf struct
+ */
+ mk_mem_free(conf_file);
+ return mk_dirhtml_theme_load();
+}
+
+/*
+ * Read the main configuration file for dirhtml: dirhtml.conf,
+ * it will alloc the dirhtml_conf struct
+*/
+int mk_dirhtml_read_config(char *path)
+{
+ unsigned long len;
+ char *default_file = NULL;
+ struct mk_rconf *conf;
+ struct mk_rconf_section *section;
+ struct file_info finfo;
+
+ mk_api->str_build(&default_file, &len, "%sdirhtml.conf", path);
+ conf = mk_api->config_open(default_file);
+ if (!conf) {
+ return -1;
+ }
+
+ section = mk_api->config_section_get(conf, "DIRLISTING");
+ if (!section) {
+ mk_err_ex(mk_api, "Could not find DIRLISTING tag in configuration file");
+ exit(EXIT_FAILURE);
+ }
+
+ /* alloc dirhtml config struct */
+ dirhtml_conf = mk_api->mem_alloc(sizeof(struct dirhtml_config));
+ dirhtml_conf->theme = mk_api->config_section_get_key(section, "Theme",
+ MK_RCONF_STR);
+ dirhtml_conf->theme_path = NULL;
+
+ mk_api->str_build(&dirhtml_conf->theme_path, &len,
+ "%sthemes/%s/", path, dirhtml_conf->theme);
+ mk_api->mem_free(default_file);
+
+ if (mk_api->file_get_info(dirhtml_conf->theme_path,
+ &finfo, MK_FILE_READ) != 0) {
+ mk_warn_ex(mk_api, "Dirlisting: cannot load theme from '%s'", dirhtml_conf->theme_path);
+ mk_warn_ex(mk_api, "Dirlisting: unloading plugin");
+ return -1;
+ }
+
+ mk_api->config_free(conf);
+ return 0;
+}
+
+int mk_dirhtml_theme_load()
+{
+ /* Data */
+ char *header, *entry, *footer;
+
+ /* Load theme files */
+ header = mk_dirhtml_load_file(MK_DIRHTML_FILE_HEADER);
+ entry = mk_dirhtml_load_file(MK_DIRHTML_FILE_ENTRY);
+ footer = mk_dirhtml_load_file(MK_DIRHTML_FILE_FOOTER);
+
+ if (!header || !entry || !footer) {
+ mk_api->mem_free(header);
+ mk_api->mem_free(entry);
+ mk_api->mem_free(footer);
+ return -1;
+ }
+
+ /* Parse themes */
+ mk_dirhtml_tpl_header = mk_dirhtml_template_create(header);
+ mk_dirhtml_tpl_entry = mk_dirhtml_template_create(entry);
+ mk_dirhtml_tpl_footer = mk_dirhtml_template_create(footer);
+
+#ifdef DEBUG_THEME
+ /* Debug data */
+ mk_dirhtml_theme_debug(&mk_dirhtml_tpl_header);
+ mk_dirhtml_theme_debug(&mk_dirhtml_tpl_entry);
+ mk_dirhtml_theme_debug(&mk_dirhtml_tpl_footer);
+
+#endif
+ mk_api->mem_free(header);
+ mk_api->mem_free(entry);
+ mk_api->mem_free(footer);
+
+ return 0;
+}
+
+#ifdef DEBUG_THEME
+int mk_dirhtml_theme_debug(struct dirhtml_template **st_tpl)
+{
+ int i = 0;
+ struct dirhtml_template *aux;
+
+ aux = *st_tpl;
+
+ printf("\n** DEBUG_THEME **");
+ fflush(stdout);
+
+ while (aux) {
+ printf("\n%i) len=%i, tag_id=%i", i, aux->len, aux->tag_id);
+ if (aux->tag_id >= 0) {
+ printf(" (%s) ", aux->tags[aux->tag_id]);
+ }
+ fflush(stdout);
+ aux = aux->next;
+ i++;
+ }
+ return 0;
+}
+#endif
+
+/* Search which tag exists first in content :
+ * ex: %_html_title_%
+ */
+static int mk_dirhtml_theme_match_tag(char *content, char *tpl[])
+{
+ int i, len, match;
+
+ for (i = 0; tpl[i]; i++) {
+ len = strlen(tpl[i]);
+ match = (int) mk_api->str_search_n(content, tpl[i], MK_STR_INSENSITIVE, len);
+ if (match >= 0) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+struct dirhtml_template *mk_dirhtml_template_create(char *content)
+{
+ int i = 0, cont_len;
+ int pos, last = 0; /* 0=search init, 1=search end */
+ int n_tags = 0, tpl_idx = 0;
+
+ char *_buf;
+ int _len;
+
+ /* Global keys */
+ char **_tpl = 0;
+
+ /* Template to return */
+ struct dirhtml_template *st_tpl = 0;
+
+ cont_len = strlen(content);
+ if (cont_len <= 0) {
+ return NULL;
+ }
+
+ /* Parsing content */
+ while (i < cont_len) {
+ pos = (int) mk_api->str_search(content + i,
+ MK_DIRHTML_TAG_INIT, MK_STR_INSENSITIVE);
+
+ if (pos < 0) {
+ break;
+ }
+
+ /* Checking global tag, if it's not found, proceed with
+ * 'entry tags'
+ */
+ _tpl = (char **) _tags_global;
+ tpl_idx = mk_dirhtml_theme_match_tag(content + i + pos, _tpl);
+
+ /* if global template do not match, use the entry tags */
+ if (tpl_idx < 0) {
+ _tpl = (char **) _tags_entry;
+ tpl_idx = mk_dirhtml_theme_match_tag(content + i + pos, _tpl);
+ }
+
+ /* if tag found is known, we add them to our list */
+ if (tpl_idx >= 0) {
+
+ _buf = mk_api->str_copy_substr(content, i, i + pos);
+ _len = strlen(_buf);
+
+ /* Dummy if/else to create or pass a created st_tpl */
+ if (!st_tpl) {
+ st_tpl = mk_dirhtml_template_list_add(NULL,
+ _buf, _len, _tpl, -1);
+ }
+ else {
+ mk_dirhtml_template_list_add(&st_tpl, _buf, _len, _tpl, -1);
+ }
+ i += (pos + strlen(_tpl[tpl_idx]));
+
+ /* This means that a value need to be replaced */
+ mk_dirhtml_template_list_add(&st_tpl, NULL, -1, _tpl, tpl_idx);
+ n_tags++;
+ }
+ else {
+ i++;
+ }
+ }
+
+ if (last < cont_len) {
+ _buf = mk_api->str_copy_substr(content, i, cont_len);
+ _len = strlen(_buf);
+
+ if (n_tags <= 0) {
+ st_tpl = mk_dirhtml_template_list_add(NULL, _buf, _len, _tpl, -1);
+ }
+ else {
+ mk_dirhtml_template_list_add(&st_tpl, _buf, _len, _tpl, -1);
+ }
+ }
+
+ return st_tpl;
+}
+
+struct dirhtml_template *mk_dirhtml_template_list_add(struct dirhtml_template **header,
+ char *buf, int len, char **tpl,
+ int tag_id)
+{
+ struct dirhtml_template *node, *aux;
+
+ node = mk_api->mem_alloc_z(sizeof(struct dirhtml_template));
+ if (!node) {
+ return NULL;
+ }
+
+ node->buf = buf;
+ node->len = len;
+ node->tag_id = tag_id;
+ node->tags = tpl;
+ node->next = NULL;
+
+ if (!header || !(*header)) {
+ return (struct dirhtml_template *) node;
+ }
+
+ aux = *header;
+ while ((*aux).next != NULL) {
+ aux = (*aux).next;
+ }
+
+ (*aux).next = node;
+ return (struct dirhtml_template *) node;
+}
+
+static int mk_dirhtml_template_len(struct dirhtml_template *tpl)
+{
+ int len = 0;
+ struct dirhtml_template *aux;
+
+ aux = tpl;
+ while (aux) {
+ len++;
+ aux = aux->next;
+ }
+
+ return len;
+}
+
+static struct mk_iov *mk_dirhtml_theme_compose(struct dirhtml_template *template,
+ struct mk_list *list)
+{
+ /*
+ * template = struct { char buf ; int len, int tag }
+ * values = struct {int tag, char *value, struct *next}
+ */
+ struct mk_iov *iov;
+ struct dirhtml_template *tpl = template;
+ struct dirhtml_value *val;
+ struct mk_list *head;
+
+ int tpl_len;
+
+ tpl_len = mk_dirhtml_template_len(template);
+
+ /* we duplicate the lenght in case we get separators */
+ iov = mk_api->iov_create(1 + tpl_len * 2, 1);
+ tpl = template;
+
+ while (tpl) {
+ /* check for dynamic value */
+ if (!tpl->buf && tpl->tag_id >= 0) {
+ mk_list_foreach(head, list) {
+ val = mk_list_entry(head, struct dirhtml_value, _head);
+ if (val->tags == tpl->tags && val->tag_id == tpl->tag_id) {
+ mk_api->iov_add(iov,
+ val->value, val->len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ val->sep.data, val->sep.len,
+ MK_FALSE);
+ break;
+ }
+ }
+ }
+ /* static */
+ else {
+ mk_api->iov_add(iov,
+ tpl->buf, tpl->len,
+ MK_FALSE);
+ }
+ tpl = tpl->next;
+ }
+
+ return iov;
+}
+
+struct dirhtml_value *mk_dirhtml_tag_assign(struct mk_list *list,
+ int tag_id, mk_ptr_t sep,
+ char *value, char **tags)
+{
+ struct dirhtml_value *aux = NULL;
+
+ aux = mk_api->mem_alloc(sizeof(struct dirhtml_value));
+ if (!aux) {
+ return NULL;
+ }
+
+ aux->tag_id = tag_id;
+ aux->value = value;
+ aux->sep = sep;
+ aux->tags = tags;
+
+ if (value) {
+ aux->len = strlen(value);
+ }
+ else {
+ aux->len = -1;
+ }
+
+ mk_list_add(&aux->_head, list);
+ return (struct dirhtml_value *) aux;
+}
+
+static void mk_dirhtml_tag_free_list(struct mk_list *list)
+{
+ struct mk_list *head;
+ struct mk_list *tmp;
+ struct dirhtml_value *target;
+
+ mk_list_foreach_safe(head, tmp, list) {
+ target = mk_list_entry(head, struct dirhtml_value, _head);
+ mk_list_del(&target->_head);
+ mk_api->mem_free(target);
+ }
+}
+
+char *mk_dirhtml_load_file(char *filename)
+{
+ char *tmp = 0, *data = 0;
+ unsigned long len;
+
+ mk_api->str_build(&tmp, &len, "%s%s", dirhtml_conf->theme_path, filename);
+
+ if (!tmp) {
+ return NULL;
+ }
+
+ data = mk_api->file_to_buffer(tmp);
+ mk_api->mem_free(tmp);
+
+ if (!data) {
+ return NULL;
+ }
+
+ return (char *) data;
+}
+
+static int mk_dirhtml_entry_cmp(const void *a, const void *b)
+{
+ struct mk_f_list *const *f_a = a;
+ struct mk_f_list *const *f_b = b;
+
+ return strcasecmp((*f_a)->name, (*f_b)->name);
+}
+
+static void mk_dirhtml_free_list(struct mk_dirhtml_request *request)
+{
+ struct mk_list *tmp;
+ struct mk_list *head;
+ struct mk_f_list *entry;
+
+ mk_list_foreach_safe(head, tmp, request->file_list) {
+ entry = mk_list_entry(head, struct mk_f_list, _head);
+ mk_list_del(&entry->_head);
+ mk_api->mem_free(entry);
+ }
+
+ mk_api->mem_free(request->file_list);
+ mk_api->mem_free(request->toc);
+}
+
+static inline struct mk_iov *enqueue_row(int i, struct mk_dirhtml_request *request)
+{
+ mk_ptr_t sep;
+ struct mk_list list;
+ struct mk_iov *iov_entry;
+
+ /* %_target_title_% */
+ if (request->toc[i]->type == DT_DIR) {
+ sep = mk_dir_iov_slash;
+ }
+ else {
+ sep = mk_dir_iov_none;
+ }
+
+ mk_list_init(&list);
+
+ /* target title */
+ mk_dirhtml_tag_assign(&list, 0, sep,
+ request->toc[i]->name,
+ (char **) _tags_entry);
+
+ /* target url */
+ mk_dirhtml_tag_assign(&list, 1, sep,
+ request->toc[i]->name, (char **) _tags_entry);
+
+ /* target name */
+ mk_dirhtml_tag_assign(&list, 2, sep,
+ request->toc[i]->name, (char **) _tags_entry);
+
+ /* target modification time */
+ mk_dirhtml_tag_assign(&list, 3, mk_dir_iov_none,
+ request->toc[i]->ft_modif, (char **) _tags_entry);
+
+ /* target size */
+ mk_dirhtml_tag_assign(&list, 4, mk_dir_iov_none,
+ request->toc[i]->size, (char **) _tags_entry);
+
+ iov_entry = mk_dirhtml_theme_compose(mk_dirhtml_tpl_entry, &list);
+
+ /* free entry list */
+ mk_dirhtml_tag_free_list(&list);
+ return iov_entry;
+}
+
+/* Release all resources for a given Request context */
+void mk_dirhtml_cleanup(struct mk_dirhtml_request *req)
+{
+ PLUGIN_TRACE("release resources");
+
+ if (req->iov_header) {
+ mk_api->iov_free(req->iov_header);
+ req->iov_header = NULL;
+ }
+ if (req->iov_entry) {
+ mk_api->iov_free(req->iov_entry);
+ req->iov_entry = NULL;
+ }
+ if (req->iov_footer) {
+ mk_api->iov_free(req->iov_footer);
+ req->iov_footer = NULL;
+ }
+ mk_dirhtml_free_list(req);
+ closedir(req->dir);
+
+ req->sr->handler_data = NULL;
+ mk_api->mem_free(req);
+ req = NULL;
+}
+
+void mk_dirhtml_cb_complete(struct mk_stream_input *in)
+{
+ struct mk_stream *stream;
+ struct mk_dirhtml_request *req;
+
+ stream = in->stream;
+ req = stream->context;
+ if (req) {
+ mk_dirhtml_cleanup(req);
+ }
+}
+
+void mk_dirhtml_cb_error(struct mk_stream *stream, int status)
+{
+#ifndef TRACE
+ (void) status;
+#endif
+ struct mk_dirhtml_request *req = stream->context;
+
+ PLUGIN_TRACE("exception: %i", status);
+
+ if (req) {
+ mk_dirhtml_cleanup(req);
+ }
+}
+
+void mk_dirhtml_cb_chunk_body_rows(struct mk_stream_input *in, long bytes)
+{
+ (void) bytes;
+
+ mk_dirhtml_cb_body_rows(in);
+}
+
+void mk_dirhtml_cb_body_rows(struct mk_stream_input *in)
+{
+ int len;
+ char tmp[16];
+ struct mk_stream *stream = in->stream;
+ struct mk_dirhtml_request *req = stream->context;
+ void (*cb_ok)(struct mk_stream_input *) = NULL;
+
+ if (req->iov_entry) {
+ mk_api->iov_free(req->iov_entry);
+ req->iov_entry = NULL;
+ }
+
+ if (req->toc_idx >= req->toc_len) {
+ if (req->chunked) {
+ len = snprintf(tmp, sizeof(tmp), "%x\r\n",
+ (int) req->iov_footer->total_len);
+ mk_stream_in_raw(req->stream,
+ NULL,
+ tmp, len,
+ NULL, NULL);
+ cb_ok = NULL;
+ }
+ else {
+ cb_ok = mk_dirhtml_cb_complete;
+ }
+
+ mk_stream_in_iov(req->stream,
+ NULL,
+ req->iov_footer,
+ NULL, NULL);
+ if (req->chunked) {
+ mk_stream_in_raw(req->stream,
+ NULL,
+ "\r\n0\r\n\r\n", 7,
+ NULL, mk_dirhtml_cb_complete);
+ }
+
+ return;
+ }
+
+ req->iov_entry = enqueue_row(req->toc_idx, req);
+ if (req->chunked) {
+ len = snprintf(tmp, sizeof(tmp), "%x\r\n",
+ (int) req->iov_entry->total_len);
+ mk_stream_in_raw(req->stream,
+ NULL,
+ tmp, len,
+ NULL, NULL);
+ cb_ok = NULL;
+ }
+ else {
+ cb_ok = mk_dirhtml_cb_body_rows;
+ }
+
+ mk_stream_in_iov(req->stream,
+ NULL,
+ req->iov_entry,
+ NULL, cb_ok);
+
+ if (req->chunked) {
+ mk_stream_in_raw(req->stream,
+ NULL,
+ "\r\n", 2,
+ mk_dirhtml_cb_chunk_body_rows, NULL);
+ }
+ req->toc_idx++;
+}
+
+/*
+ * The HTTP Headers were sent, now start registering the
+ * rows for each directory entry.
+ */
+void cb_header_finish(struct mk_stream_input *in)
+{
+ struct mk_stream *stream = in->stream;
+ struct mk_dirhtml_request *req;
+
+ req = stream->context;
+ if (req->iov_header) {
+ mk_api->iov_free(req->iov_header);
+ req->iov_header = NULL;
+ }
+ mk_dirhtml_cb_body_rows(in);
+}
+
+static int mk_dirhtml_init(struct mk_plugin *plugin,
+ struct mk_http_session *cs, struct mk_http_request *sr)
+{
+ DIR *dir;
+ int len;
+ char tmp[16];
+ unsigned int i = 0;
+ struct mk_list *head;
+ struct mk_list list;
+ struct mk_f_list *entry;
+ struct mk_dirhtml_request *request;
+ struct mk_stream *stream;
+
+ if (!(dir = opendir(sr->real_path.data))) {
+ return -1;
+ }
+
+ /* Create the main context */
+ request = mk_api->mem_alloc(sizeof(struct mk_dirhtml_request));
+ if (!request) {
+ closedir(dir);
+ return -1;
+ }
+
+ stream = mk_stream_set(NULL, cs->channel, request,
+ NULL, NULL, mk_dirhtml_cb_error);
+ if (!stream) {
+ closedir(dir);
+ free(request);
+ return -1;
+ }
+
+ request->stream = stream;
+ request->state = MK_DIRHTML_STATE_HTTP_HEADER;
+ request->dir = dir;
+ request->toc_idx = 0;
+ request->cs = cs;
+ request->sr = sr;
+ request->toc_len = 0;
+ request->chunked = MK_FALSE;
+ request->iov_header = NULL;
+ request->iov_entry = NULL;
+ request->iov_footer = NULL;
+
+ sr->handler_data = request;
+
+ request->file_list = mk_dirhtml_create_list(dir, sr->real_path.data,
+ &request->toc_len);
+
+ /* Building headers */
+ mk_api->header_set_http_status(sr, MK_HTTP_OK);
+ sr->headers.cgi = SH_CGI;
+ sr->headers.breakline = MK_HEADER_BREAKLINE;
+ sr->headers.content_type = mk_dirhtml_default_mime;
+ sr->headers.content_length = -1;
+
+ if (sr->protocol >= MK_HTTP_PROTOCOL_11) {
+ sr->headers.transfer_encoding = MK_HEADER_TE_TYPE_CHUNKED;
+ request->chunked = MK_TRUE;
+ }
+
+ /*
+ * Creating response template
+ */
+
+ mk_list_init(&list);
+
+ /* Set %_html_title_% */
+ mk_dirhtml_tag_assign(&list, 0, mk_dir_iov_none,
+ sr->uri_processed.data,
+ (char **) _tags_global);
+
+ /* Set %_theme_path_% */
+ mk_dirhtml_tag_assign(&list, 1, mk_dir_iov_none,
+ dirhtml_conf->theme_path, (char **) _tags_global);
+
+ /* HTML Header */
+ request->iov_header = mk_dirhtml_theme_compose(mk_dirhtml_tpl_header,
+ &list);
+
+ /* HTML Footer */
+ request->iov_footer = mk_dirhtml_theme_compose(mk_dirhtml_tpl_footer,
+ &list);
+ mk_dirhtml_tag_free_list(&list);
+
+ /* Creating table of contents and sorting */
+ request->toc = mk_api->mem_alloc(sizeof(struct mk_f_list *) * request->toc_len);
+
+ i = 0;
+ mk_list_foreach(head, request->file_list) {
+ entry = mk_list_entry(head, struct mk_f_list, _head);
+ request->toc[i] = entry;
+ i++;
+ }
+
+ qsort(request->toc,
+ request->toc_len,
+ sizeof(*request->toc),
+ mk_dirhtml_entry_cmp);
+
+ /* Prepare HTTP response headers */
+ mk_api->header_prepare(plugin, cs, sr);
+
+ if (request->chunked) {
+ len = snprintf(tmp, sizeof(tmp), "%x\r\n",
+ (int) request->iov_header->total_len);
+ mk_stream_in_raw(request->stream,
+ NULL,
+ tmp, len,
+ NULL, mk_dirhtml_cb_complete);
+ }
+
+ mk_stream_in_iov(request->stream,
+ NULL,
+ request->iov_header,
+ NULL, cb_header_finish);
+
+ if (request->chunked) {
+ mk_stream_in_raw(request->stream,
+ NULL,
+ "\r\n", 2,
+ NULL, NULL);
+ }
+ return 0;
+}
+
+int mk_dirlisting_plugin_init(struct mk_plugin *plugin, char *confdir)
+{
+ mk_api = plugin->api;
+
+ return mk_dirhtml_conf(confdir);
+}
+
+int mk_dirlisting_plugin_exit(struct mk_plugin *plugin)
+{
+ (void) plugin;
+
+ mk_api->mem_free(dirhtml_conf->theme);
+ mk_api->mem_free(dirhtml_conf->theme_path);
+ mk_api->mem_free(dirhtml_conf);
+
+ return 0;
+}
+
+int mk_dirlisting_stage30(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ int n_param,
+ struct mk_list *params)
+{
+ (void) plugin;
+ (void) n_param;
+ (void) params;
+
+ /* validate file_info */
+ if (sr->file_info.size == 0) {
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+
+ /* This plugin just handle directories */
+ if (sr->file_info.is_directory == MK_FALSE) {
+ return MK_PLUGIN_RET_NOT_ME;
+ }
+
+ PLUGIN_TRACE("Dirlisting attending socket %i", cs->socket);
+ if (mk_dirhtml_init(plugin, cs, sr)) {
+ /*
+ * If we failed here, we cannot return RET_END - that causes a mk_bug.
+ * dirhtml_init only fails if opendir fails. Usually we're at full
+ * capacity then and can't open new files.
+ */
+ return MK_PLUGIN_RET_CLOSE_CONX;
+ }
+
+ return MK_PLUGIN_RET_END;
+}
+
+int mk_dirlisting_stage30_hangup(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr)
+{
+ (void) cs;
+ (void) plugin;
+
+ if (sr->handler_data) {
+ mk_dirhtml_cleanup(sr->handler_data);
+ }
+ return 0;
+}
+
+struct mk_plugin_stage mk_plugin_stage_dirlisting = {
+ .stage30 = &mk_dirlisting_stage30,
+ .stage30_hangup = &mk_dirlisting_stage30_hangup
+};
+
+struct mk_plugin mk_plugin_dirlisting = {
+ /* Identification */
+ .shortname = "dirlisting",
+ .name = "Directory Listing",
+ .version = MK_VERSION_STR,
+ .hooks = MK_PLUGIN_STAGE,
+
+ /* Init / Exit */
+ .init_plugin = mk_dirlisting_plugin_init,
+ .exit_plugin = mk_dirlisting_plugin_exit,
+
+ /* Init Levels */
+ .master_init = NULL,
+ .worker_init = NULL,
+
+ /* Type */
+ .stage = &mk_plugin_stage_dirlisting
+};
diff --git a/fluent-bit/lib/monkey/plugins/dirlisting/dirlisting.h b/fluent-bit/lib/monkey/plugins/dirlisting/dirlisting.h
new file mode 100644
index 000000000..678a4887b
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/dirlisting/dirlisting.h
@@ -0,0 +1,181 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+/* dir_html.c */
+#ifndef MK_DIRHTML_H
+#define MK_DIRHTML_H
+
+#include <dirent.h>
+#include <limits.h>
+
+#define MK_DIRHTML_URL "/_mktheme"
+#define MK_DIRHTML_DEFAULT_MIME "Content-Type: text/html\r\n"
+
+/* For every directory requested, don't send more than
+ * this limit of entries.
+ */
+#define MK_DIRHTML_BUFFER_LIMIT 30
+#define MK_DIRHTML_BUFFER_GROW 5
+
+#define MK_HEADER_CHUNKED "Transfer-Encoding: Chunked\r\n\r\n"
+#define MK_DIRHTML_FMOD_LEN 24
+
+/* Theme files */
+#define MK_DIRHTML_FILE_HEADER "header.theme"
+#define MK_DIRHTML_FILE_ENTRY "entry.theme"
+#define MK_DIRHTML_FILE_FOOTER "footer.theme"
+
+#define MK_DIRHTML_TAG_INIT "%_"
+#define MK_DIRHTML_TAG_END "_%"
+#define MK_DIRHTML_SIZE_DIR "-"
+
+/* Stream state */
+#define MK_DIRHTML_STATE_HTTP_HEADER 0
+#define MK_DIRHTML_STATE_TPL_HEADER 1
+#define MK_DIRHTML_STATE_BODY 2
+#define MK_DIRHTML_STATE_FOOTER 3
+
+char *_tags_global[] = { "%_html_title_%",
+ "%_theme_path_%",
+ NULL
+};
+
+char *_tags_entry[] = { "%_target_title_%",
+ "%_target_url_%",
+ "%_target_name_%",
+ "%_target_time_%",
+ "%_target_size_%",
+ NULL
+};
+
+struct plugin_api *mk_api;
+
+struct mk_f_list
+{
+ char ft_modif[MK_DIRHTML_FMOD_LEN];
+ struct file_info info;
+ char name[NAME_MAX + 1]; /* The name can be up to NAME_MAX long; include NULL. */
+ char size[16];
+ unsigned char type;
+
+ struct mk_list _head;
+};
+
+/* Main configuration of dirhtml module */
+struct dirhtml_config
+{
+ char *theme;
+ char *theme_path;
+};
+
+/* Represent a request context */
+struct mk_dirhtml_request
+{
+ /* State */
+ int state;
+ int chunked;
+
+ /* Target directory */
+ DIR *dir;
+
+ /* Table of Content */
+ unsigned int toc_idx;
+ unsigned long toc_len;
+ struct mk_f_list **toc;
+ struct mk_list *file_list;
+
+ /* Stream handler */
+ struct mk_stream *stream;
+
+ /* Reference IOV stuff */
+ struct mk_iov *iov_header;
+ struct mk_iov *iov_entry;
+ struct mk_iov *iov_footer;
+
+ /* Session data */
+ struct mk_http_session *cs;
+ struct mk_http_request *sr;
+};
+
+
+extern const mk_ptr_t mk_dirhtml_default_mime;
+extern const mk_ptr_t mk_iov_dash;
+
+/* Global config */
+struct dirhtml_config *dirhtml_conf;
+
+/* Used to keep splitted content of every template */
+struct dirhtml_template
+{
+ char *buf;
+ int tag_id;
+ int len;
+ struct dirhtml_template *next;
+ char **tags; /* array of theme tags: [%_xaa__%, %_xyz_%] */
+};
+
+/* Templates for header, entries and footer */
+struct dirhtml_template *mk_dirhtml_tpl_header;
+struct dirhtml_template *mk_dirhtml_tpl_entry;
+struct dirhtml_template *mk_dirhtml_tpl_footer;
+
+struct dirhtml_value
+{
+ int tag_id;
+ mk_ptr_t sep; /* separator code after value */
+
+ /* string data */
+ int len;
+ char *value;
+
+ /* next node */
+ struct mk_list _head;
+
+ char **tags; /* array of tags which values correspond */
+};
+
+struct dirhtml_value *mk_dirhtml_value_global;
+
+/* Configuration struct */
+struct mk_config *conf;
+
+char *check_string(char *str);
+char *read_header_footer_file(char *file_path);
+
+int mk_dirhtml_conf();
+char *mk_dirhtml_load_file(char *filename);
+
+struct dirhtml_template *mk_dirhtml_template_create(char *content);
+
+struct dirhtml_template
+ *mk_dirhtml_template_list_add(struct dirhtml_template **header,
+ char *buf, int len, char **tpl, int tag);
+
+int mk_dirhtml_read_config(char *path);
+int mk_dirhtml_theme_load();
+int mk_dirhtml_theme_debug(struct dirhtml_template **st_tpl);
+
+struct dirhtml_value *mk_dirhtml_tag_assign(struct mk_list *list,
+ int tag_id, mk_ptr_t sep,
+ char *value, char **tags);
+
+struct f_list *get_dir_content(struct mk_http_request *sr, char *path);
+
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/fastcgi/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/fastcgi/CMakeLists.txt
new file mode 100644
index 000000000..46056eead
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/fastcgi/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(src
+ fastcgi.c
+ fcgi_handler.c
+ )
+
+MONKEY_PLUGIN(fastcgi "${src}")
+add_subdirectory(conf)
diff --git a/fluent-bit/lib/monkey/plugins/fastcgi/conf/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/fastcgi/conf/CMakeLists.txt
new file mode 100644
index 000000000..0623bf68b
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/fastcgi/conf/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(conf_dir "${MK_PATH_CONF}/plugins/fastcgi/")
+
+install(DIRECTORY DESTINATION ${conf_dir})
+
+if(BUILD_LOCAL)
+ file(COPY fastcgi.conf DESTINATION ${conf_dir})
+else()
+ install(FILES fastcgi.conf DESTINATION ${conf_dir})
+endif()
diff --git a/fluent-bit/lib/monkey/plugins/fastcgi/conf/fastcgi.conf b/fluent-bit/lib/monkey/plugins/fastcgi/conf/fastcgi.conf
new file mode 100644
index 000000000..c5651c748
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/fastcgi/conf/fastcgi.conf
@@ -0,0 +1,16 @@
+# FastCGI
+# =======
+# To enable this plugin you'll need at least one [FASTCGI_SERVER].
+#
+# This configuration handles php scripts using php5-fpm running on
+# localhost or over the network.
+
+[FASTCGI_SERVER]
+ # Each server must have a unique name, this is mandatory.
+ ServerName php5-fpm1
+
+ # Depending on your version of php5-fpm, one of these should be
+ # enabled.
+ #
+ # ServerAddr 127.0.0.1:9000
+ ServerPath /var/run/php5-fpm.sock
diff --git a/fluent-bit/lib/monkey/plugins/fastcgi/fastcgi.c b/fluent-bit/lib/monkey/plugins/fastcgi/fastcgi.c
new file mode 100644
index 000000000..1c0e716a2
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/fastcgi/fastcgi.c
@@ -0,0 +1,233 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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 <monkey/mk_api.h>
+
+#include "fastcgi.h"
+#include "fcgi_handler.h"
+
+static int mk_fastcgi_config(char *path)
+{
+ int ret;
+ int sep;
+ char *file = NULL;
+ char *cnf_srv_name = NULL;
+ char *cnf_srv_addr = NULL;
+ char *cnf_srv_port = NULL;
+ char *cnf_srv_path = NULL;
+ unsigned long len;
+ struct file_info finfo;
+ struct mk_rconf *conf;
+ struct mk_rconf_section *section;
+
+ mk_api->str_build(&file, &len, "%sfastcgi.conf", path);
+ conf = mk_api->config_open(file);
+ if (!conf) {
+ return -1;
+ }
+
+ section = mk_api->config_section_get(conf, "FASTCGI_SERVER");
+ if (!section) {
+ return -1;
+ }
+
+ /* Get section values */
+ cnf_srv_name = mk_api->config_section_get_key(section,
+ "ServerName",
+ MK_RCONF_STR);
+ cnf_srv_addr = mk_api->config_section_get_key(section,
+ "ServerAddr",
+ MK_RCONF_STR);
+ cnf_srv_path = mk_api->config_section_get_key(section,
+ "ServerPath",
+ MK_RCONF_STR);
+
+ /* Validations */
+ if (!cnf_srv_name) {
+ mk_warn("[fastcgi] Invalid ServerName in configuration.");
+ return -1;
+ }
+
+ /* Split the address, try to lookup the TCP port */
+ if (cnf_srv_addr) {
+ sep = mk_api->str_char_search(cnf_srv_addr, ':', strlen(cnf_srv_addr));
+ if (sep <= 0) {
+ mk_warn("[fastcgi] Missing TCP port con ServerAddress key");
+ return -1;
+ }
+
+ cnf_srv_port = mk_api->str_dup(cnf_srv_addr + sep + 1);
+ cnf_srv_addr[sep] = '\0';
+ }
+
+ /* Just one mode can exist (for now) */
+ if (cnf_srv_path && cnf_srv_addr) {
+ mk_warn("[fastcgi] Use ServerAddr or ServerPath, not both");
+ return -1;
+ }
+
+ /* Unix socket path */
+ if (cnf_srv_path) {
+ ret = mk_api->file_get_info(cnf_srv_path, &finfo, MK_FILE_READ);
+ if (ret == -1) {
+ mk_warn("[fastcgi] Cannot open unix socket: %s", cnf_srv_path);
+ return -1;
+ }
+ }
+
+ /* Set the global configuration */
+ fcgi_conf.server_name = cnf_srv_name;
+ fcgi_conf.server_addr = cnf_srv_addr;
+ fcgi_conf.server_port = cnf_srv_port;
+ fcgi_conf.server_path = cnf_srv_path;
+
+ return 0;
+}
+
+
+/* Entry point for thread/co-routine */
+static void mk_fastcgi_stage30_thread(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ int n_params,
+ struct mk_list *params)
+{
+ struct fcgi_handler *handler;
+ (void) plugin;
+ (void) n_params;
+ (void) params;
+
+ printf("entering thread\n");
+ handler = fcgi_handler_new(cs, sr);
+ if (!handler) {
+ fprintf(stderr, "Could not create handler");
+ }
+}
+
+/* Callback handler */
+int mk_fastcgi_stage30(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ int n_params,
+ struct mk_list *params)
+{
+ (void) n_params;
+ (void) params;
+ struct fcgi_handler *handler;
+
+ /*
+ * This plugin uses the Monkey Thread model (co-routines), for hence
+ * upon return MK_PLUGIN_RET_CONTINUE, Monkey core will create a
+ * new thread (co-routine) and defer the control to the stage30_thread
+ * callback function (mk_fastcgi_stage30_thread).
+ *
+ * We don't do any validation, so we are OK with MK_PLUGIN_RET_CONTINUE.
+ */
+
+ return MK_PLUGIN_RET_CONTINUE;
+
+ ret = mk_fastcgi_start_processing(cs, sr);
+ if (ret == 0) {
+ return MK_PLUGIN_RET_CONTINUE;
+ }
+
+ return MK_PLUGIN_RET_CONTINUE;
+}
+
+int mk_fastcgi_stage30_hangup(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr)
+{
+ (void) plugin;
+ (void) cs;
+ struct fcgi_handler *handler;
+
+ handler = sr->handler_data;
+ if (!handler) {
+ return -1;
+ }
+
+ if (handler->hangup == MK_TRUE) {
+ return 0;
+ }
+
+ handler->active = MK_FALSE;
+ handler->hangup = MK_TRUE;
+
+ fcgi_exit(sr->handler_data);
+
+ return 0;
+}
+
+int mk_fastcgi_plugin_init(struct plugin_api **api, char *confdir)
+{
+ int ret;
+
+ mk_api = *api;
+
+ /* read global configuration */
+ ret = mk_fastcgi_config(confdir);
+ if (ret == -1) {
+ mk_warn("[fastcgi] configuration error/missing, plugin disabled.");
+ }
+ return ret;
+}
+
+int mk_fastcgi_plugin_exit()
+{
+ return 0;
+}
+
+int mk_fastcgi_master_init(struct mk_server *server)
+{
+ (void) server;
+ return 0;
+}
+
+void mk_fastcgi_worker_init()
+{
+}
+
+struct mk_plugin_stage mk_plugin_stage_fastcgi = {
+ .stage30 = &mk_fastcgi_stage30,
+ .stage30_thread = &mk_fastcgi_stage30_thread,
+ .stage30_hangup = &mk_fastcgi_stage30_hangup
+};
+
+struct mk_plugin mk_plugin_fastcgi = {
+ /* Identification */
+ .shortname = "fastcgi",
+ .name = "FastCGI Client",
+ .version = "1.0",
+ .hooks = MK_PLUGIN_STAGE,
+
+ /* Init / Exit */
+ .init_plugin = mk_fastcgi_plugin_init,
+ .exit_plugin = mk_fastcgi_plugin_exit,
+
+ /* Init Levels */
+ .master_init = mk_fastcgi_master_init,
+ .worker_init = mk_fastcgi_worker_init,
+
+ /* Type */
+ .stage = &mk_plugin_stage_fastcgi,
+
+ /* Flags */
+ .flags = MK_PLUGIN_THREAD
+};
diff --git a/fluent-bit/lib/monkey/plugins/fastcgi/fastcgi.h b/fluent-bit/lib/monkey/plugins/fastcgi/fastcgi.h
new file mode 100644
index 000000000..a367f761c
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/fastcgi/fastcgi.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#ifndef MK_FASTCGI_H
+#define MK_FASTCGI_H
+
+struct mk_fcgi_conf {
+ char *server_name;
+
+ /* Unix Socket */
+ char *server_path;
+
+ /* TCP Server */
+ char *server_addr;
+ char *server_port;
+};
+
+struct mk_fcgi_conf fcgi_conf;
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/fastcgi/fcgi_handler.c b/fluent-bit/lib/monkey/plugins/fastcgi/fcgi_handler.c
new file mode 100644
index 000000000..0c7881309
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/fastcgi/fcgi_handler.c
@@ -0,0 +1,967 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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 <monkey/mk_api.h>
+#include <monkey/mk_net.h>
+#include <monkey/mk_stream.h>
+
+#include "fastcgi.h"
+#include "fcgi_handler.h"
+
+#define FCGI_BUF(h) (char *) h->buf_data + h->buf_len
+#define FCGI_PARAM_DYN(str) str, strlen(str), MK_FALSE
+#define FCGI_PARAM_CONST(str) str, sizeof(str) -1, MK_FALSE
+#define FCGI_PARAM_PTR(ptr) ptr.data, ptr.len, MK_FALSE
+#define FCGI_PARAM_DUP(str) mk_api->str_dup(str), strlen(str), MK_TRUE
+
+int fcgi_pad[256] = {0};
+
+static inline void fcgi_build_header(struct fcgi_record_header *rec,
+ uint8_t type, uint16_t request_id,
+ uint16_t content_length)
+{
+ rec->version = FCGI_VERSION_1;
+ rec->type = type;
+ fcgi_encode16(&rec->request_id, request_id);
+ fcgi_encode16(&rec->content_length, content_length);
+ rec->padding_length = 0;
+ rec->reserved = 0;
+}
+
+static inline void fcgi_build_request_body(struct fcgi_begin_request_body *body)
+{
+ fcgi_encode16(&body->role, FCGI_RESPONDER);
+ body->flags = 0;
+ memset(body->reserved, '\0', sizeof(body->reserved));
+}
+
+static inline size_t fcgi_write_length(char *p, size_t len)
+{
+ if (len < 127) {
+ *p++ = len;
+ return 1;
+ }
+ else{
+ *p++ = (len >> 24) | 0x80;
+ *p++ = (len >> 16) & 0xff;
+ *p++ = (len >> 8) & 0xff;
+ *p++ = (len) & 0xff;
+ return 4;
+ }
+}
+
+static inline int fcgi_add_param_empty(struct fcgi_handler *handler)
+{
+ char *p;
+
+ p = FCGI_BUF(handler);
+ fcgi_build_header((struct fcgi_record_header *) p, FCGI_PARAMS, 1, 0);
+ mk_api->iov_add(handler->iov, p,
+ sizeof(struct fcgi_record_header), MK_FALSE);
+ handler->buf_len += sizeof(struct fcgi_record_header);
+ return 0;
+}
+
+static inline int fcgi_add_param(struct fcgi_handler *handler,
+ char *key, int key_len, int key_free,
+ char *val, int val_len, int val_free)
+{
+ int ret;
+ int len;
+ int diff;
+ int padding;
+ char *p;
+ char *buf;
+ struct fcgi_record_header *h;
+
+ buf = p = (char * ) handler->buf_data + handler->buf_len;
+
+ len = key_len + val_len;
+ len += key_len > 127 ? 4 : 1;
+ len += val_len > 127 ? 4 : 1;
+
+ fcgi_build_header((struct fcgi_record_header *) p, FCGI_PARAMS, 1, len);
+ padding = ~(len - 1) & 7;
+ if (padding) {
+ h = (struct fcgi_record_header *) p;
+ h->padding_length = padding;
+ }
+
+ p += sizeof(struct fcgi_record_header);
+ p += fcgi_write_length(p, key_len);
+ p += fcgi_write_length(p, val_len);
+
+ diff = (p - buf);
+ handler->buf_len += diff;
+ ret = mk_api->iov_add(handler->iov, buf, diff, MK_FALSE);
+ if (ret == -1) {
+ return -1;
+ }
+
+ mk_api->iov_add(handler->iov, key, key_len, key_free);
+ mk_api->iov_add(handler->iov, val, val_len, val_free);
+
+ if (padding) {
+ mk_api->iov_add(handler->iov, fcgi_pad, h->padding_length, MK_FALSE);
+ }
+
+ return 0;
+}
+
+static inline int fcgi_add_param_http_header(struct fcgi_handler *handler,
+ struct mk_http_header *header)
+{
+ unsigned int i;
+ int avail;
+ int req;
+ int diff;
+ char *p;
+ char *buf;
+
+ avail = (sizeof(handler->buf_data) - handler->buf_len);
+ req = sizeof(struct fcgi_record_header) + 8;
+ req += header->key.len + 5;
+
+ if (avail < req) {
+ return -1;
+ }
+
+ buf = p = (handler->buf_data + handler->buf_len);
+ *p++ = 'H';
+ *p++ = 'T';
+ *p++ = 'T';
+ *p++ = 'P';
+ *p++ = '_';
+
+ for (i = 0; i < header->key.len; i++) {
+ if (header->key.data[i] == '-') {
+ *p++ = '_';
+ }
+ else {
+ *p++ = toupper(header->key.data[i]);
+ }
+ }
+
+ diff = (p - buf);
+ handler->buf_len += diff;
+ fcgi_add_param(handler,
+ buf, diff, MK_FALSE,
+ header->val.data, header->val.len, MK_FALSE);
+
+ return 0;
+}
+
+static inline int fcgi_add_param_net(struct fcgi_handler *handler)
+{
+ int ret;
+ const char *p;
+ char buffer[256];
+
+ /* This is to identify whether its IPV4 or IPV6 */
+ struct sockaddr_storage addr;
+ int port = 0;
+ socklen_t addr_len = sizeof(struct sockaddr_in);
+
+ ret = getsockname(handler->cs->socket, (struct sockaddr *)&addr, &addr_len);
+ if (ret == -1) {
+#ifdef TRACE
+ perror("getsockname");
+#endif
+ if (errno == EBADF) {
+ MK_TRACE("[fastcgi=%i] network connection broken",
+ handler->cs->socket);
+ }
+ return -1;
+ }
+
+ if (addr.ss_family == AF_INET) {
+ struct sockaddr_in *s = (struct sockaddr_in *)&addr;
+ port = ntohs(s->sin_port);
+ p = inet_ntop(AF_INET, &s->sin_addr, buffer, sizeof(buffer));
+ if (!p) {
+ perror("inet_ntop");
+ return -1;
+ }
+ } else { /* AF_INET6 */
+ struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
+ port = ntohs(s->sin6_port);
+ p = inet_ntop(AF_INET6, &s->sin6_addr, buffer, sizeof(buffer));
+ if (!p) {
+ perror("inet_ntop");
+ return -1;
+ }
+ }
+
+ /* Server Address */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("SERVER_ADDR"),
+ FCGI_PARAM_DUP(buffer));
+
+ /* Server Port */
+ snprintf(buffer, 256, "%d", port);
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("SERVER_PORT"),
+ FCGI_PARAM_DUP(buffer));
+
+
+ ret = getpeername(handler->cs->socket, (struct sockaddr *)&addr, &addr_len);
+ if (ret == -1) {
+ perror("getpeername");
+ return -1;
+ }
+
+ if (addr.ss_family == AF_INET) {
+ struct sockaddr_in *s = (struct sockaddr_in *)&addr;
+ port = ntohs(s->sin_port);
+ p = inet_ntop(AF_INET, &s->sin_addr, buffer, sizeof(buffer));
+ if (!p) {
+ perror("inet_ntop");
+ return -1;
+ }
+ } else { /* AF_INET6 */
+ struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
+ port = ntohs(s->sin6_port);
+
+ if (IN6_IS_ADDR_V4MAPPED(&s->sin6_addr)) {
+ /* This is V4-Mapped-V6 - Lets convert it to plain IPV4 address.
+ * E.g. we would have received like this ::ffff:10.106.146.73.
+ * This would be converted to 10.106.146.73.
+ */
+ struct sockaddr_in addr4;
+ struct sockaddr_in *s4 = (struct sockaddr_in *)&addr4;
+ memset(&addr4, 0, sizeof(addr4));
+ addr4.sin_family = AF_INET;
+ addr4.sin_port = &s->sin6_port;
+ memcpy(&addr4.sin_addr.s_addr,
+ s->sin6_addr.s6_addr + 12,
+ sizeof(addr4.sin_addr.s_addr));
+ p = inet_ntop(AF_INET, &s4->sin_addr, buffer, sizeof(buffer));
+ if (!p) {
+ perror("inet_ntop");
+ return -1;
+ }
+ } else {
+ p = inet_ntop(AF_INET6, &s->sin6_addr, buffer, sizeof(buffer));
+ if (!p) {
+ perror("inet_ntop");
+ return -1;
+ }
+ }
+ }
+
+ /* Remote Addr */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("REMOTE_ADDR"),
+ FCGI_PARAM_DUP(buffer));
+
+ /* Remote Port */
+ snprintf(buffer, 256, "%d", port);
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("REMOTE_PORT"),
+ FCGI_PARAM_DUP(buffer));
+
+ return 0;
+}
+
+static inline int fcgi_stdin_chunk(struct fcgi_handler *handler)
+{
+ int padding = 0;
+ uint16_t max = 65535;
+ uint16_t chunk;
+ uint64_t total;
+ char *p;
+ char *eof;
+ struct fcgi_record_header *h;
+
+ total = handler->stdin_length - handler->stdin_offset;
+ if (total > max) {
+ chunk = max;
+ }
+ else {
+ chunk = total;
+ }
+
+ p = FCGI_BUF(handler);
+ h = (struct fcgi_record_header *) p;
+ fcgi_build_header(h, FCGI_STDIN, 1, chunk);
+ h->padding_length = ~(chunk - 1) & 7;
+
+ MK_TRACE("[fastcgi] STDIN: length=%i", chunk);
+
+ mk_api->iov_add(handler->iov, p, FCGI_RECORD_HEADER_SIZE, MK_FALSE);
+ handler->buf_len += FCGI_RECORD_HEADER_SIZE;
+
+
+ if (chunk > 0) {
+ mk_api->iov_add(handler->iov,
+ handler->stdin_buffer + handler->stdin_offset,
+ chunk,
+ MK_FALSE);
+ }
+
+ if (h->padding_length > 0) {
+ mk_api->iov_add(handler->iov,
+ fcgi_pad, h->padding_length,
+ MK_FALSE);
+ }
+
+ if (handler->stdin_offset + chunk == handler->stdin_length) {
+ eof = FCGI_BUF(handler);
+ fcgi_build_header((struct fcgi_record_header *) eof, FCGI_STDIN, 1, 0);
+ mk_api->iov_add(handler->iov, eof, FCGI_RECORD_HEADER_SIZE, MK_FALSE);
+ handler->buf_len += FCGI_RECORD_HEADER_SIZE + padding;
+ }
+
+ handler->stdin_offset += chunk;
+ return 0;
+}
+
+static inline int fcgi_add_stdin(struct fcgi_handler *handler)
+{
+ uint64_t bytes = handler->sr->data.len;
+
+ if (bytes <= 0) {
+ return -1;
+ }
+
+ handler->stdin_length = bytes;
+ handler->stdin_offset = 0;
+ handler->stdin_buffer = handler->sr->data.data;
+ fcgi_stdin_chunk(handler);
+
+ return 0;
+}
+
+static int fcgi_encode_request(struct fcgi_handler *handler)
+{
+ int ret;
+ struct mk_http_header *header;
+ struct fcgi_begin_request_record *request;
+
+ MK_TRACE("ENCODE REQUEST");
+
+ request = &handler->header_request;
+ fcgi_build_header(&request->header, FCGI_BEGIN_REQUEST, 1,
+ FCGI_BEGIN_REQUEST_BODY_SIZE);
+
+ fcgi_build_request_body(&request->body);
+
+ /* BEGIN_REQUEST */
+ mk_api->iov_add(handler->iov,
+ &handler->header_request,
+ sizeof(handler->header_request),
+ MK_FALSE);
+
+ /* Server Software */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("GATEWAY_INTERFACE"),
+ FCGI_PARAM_CONST("CGI/1.1"));
+
+ /* Server Software */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("REDIRECT_STATUS"),
+ FCGI_PARAM_CONST("200"));
+
+ /* Server Software */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("SERVER_SOFTWARE"),
+ FCGI_PARAM_DYN(mk_api->config->server_signature));
+
+ /* Server Name */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("SERVER_PROTOCOL"),
+ FCGI_PARAM_CONST("HTTP/1.1"));
+
+ /* Server Name */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("SERVER_NAME"),
+ handler->sr->host_alias->name,
+ handler->sr->host_alias->len,
+ MK_FALSE);
+
+ /* Document Root */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("DOCUMENT_ROOT"),
+ FCGI_PARAM_PTR(handler->sr->host_conf->documentroot));
+
+ /* Network params: SERVER_ADDR, SERVER_PORT, REMOTE_ADDR & REMOTE_PORT */
+ ret = fcgi_add_param_net(handler);
+ if (ret == -1) {
+ return -1;
+ }
+
+ /* Script Filename */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("SCRIPT_FILENAME"),
+ FCGI_PARAM_PTR(handler->sr->real_path));
+
+ /* Script Filename */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("SCRIPT_NAME"),
+ FCGI_PARAM_PTR(handler->sr->uri_processed));
+
+ /* Request Method */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("REQUEST_METHOD"),
+ FCGI_PARAM_PTR(handler->sr->method_p));
+
+
+ /* Request URI */
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("REQUEST_URI"),
+ FCGI_PARAM_PTR(handler->sr->uri));
+
+ /* Query String */
+ if (handler->sr->query_string.len > 0) {
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("QUERY_STRING"),
+ FCGI_PARAM_PTR(handler->sr->query_string));
+ }
+
+ /* HTTPS */
+ if (MK_SCHED_CONN_PROP(handler->cs->conn) & MK_CAP_SOCK_TLS) {
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("HTTPS"),
+ FCGI_PARAM_CONST("on"));
+ }
+
+ /* Content Length */
+ if (handler->sr->_content_length.data) {
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("CONTENT_LENGTH"),
+ FCGI_PARAM_PTR(handler->sr->_content_length));
+ }
+
+ /* Content Length */
+ header = &handler->cs->parser.headers[MK_HEADER_CONTENT_TYPE];
+ if (header->type == MK_HEADER_CONTENT_TYPE) {
+ fcgi_add_param(handler,
+ FCGI_PARAM_CONST("CONTENT_TYPE"),
+ FCGI_PARAM_PTR(header->val));
+
+ }
+
+ /* Append HTTP request headers */
+ struct mk_list *head;
+ struct mk_http_header *http_header;
+ mk_list_foreach(head, &handler->cs->parser.header_list) {
+ http_header = mk_list_entry(head, struct mk_http_header, _head);
+ fcgi_add_param_http_header(handler, http_header);
+ }
+
+ /* Append the empty params record */
+ fcgi_add_param_empty(handler);
+
+ /* Data for FCGI_STDIN */
+ fcgi_add_stdin(handler);
+
+ return 0;
+}
+
+size_t fcgi_read_header(void *p, struct fcgi_record_header *h)
+{
+ memcpy(h, p, sizeof(struct fcgi_record_header));
+ h->request_id = htons(h->request_id);
+ h->content_length = htons(h->content_length);
+
+ return sizeof(*h);
+}
+
+static inline int fcgi_buffer_consume(struct fcgi_handler *handler, size_t bytes)
+{
+ if (bytes >= handler->buf_len) {
+ handler->buf_len = 0;
+ return 0;
+ }
+
+ memmove(handler->buf_data, handler->buf_data + bytes,
+ handler->buf_len - bytes);
+ handler->buf_len -= bytes;
+ return 0;
+}
+
+static char *getearliestbreak(const char buf[], const unsigned bufsize,
+ unsigned char * const advance)
+{
+ char *crend;
+ char *lfend;
+
+ crend = memmem(buf, bufsize, "\r\n\r\n", 4);
+ lfend = memmem(buf, bufsize, "\n\n", 2);
+
+ if (!crend && !lfend)
+ return NULL;
+
+ /* If only one found, return that one */
+ if (!crend) {
+ *advance = 2;
+ return lfend;
+ }
+ if (!lfend)
+ return crend;
+
+ /* Both found, return the earlier one - the latter one is part of content */
+ if (lfend < crend) {
+ *advance = 2;
+ return lfend;
+ }
+ return crend;
+}
+
+static int fcgi_write(struct fcgi_handler *handler, char *buf, size_t len)
+{
+ mk_stream_in_raw(handler->stream,
+ NULL,
+ buf, len,
+ NULL, NULL);
+
+ if (handler->headers_set == MK_TRUE) {
+ mk_stream_in_raw(handler->stream,
+ NULL,
+ "\r\n", 2,
+ NULL, NULL);
+ }
+ return 0;
+}
+
+void fcgi_stream_eof(struct mk_stream_input *in)
+{
+ (void) in;
+ // FIXME
+ //struct fcgi_handler *handler;
+
+ //handler = stream->data;
+ //if (handler->hangup == MK_FALSE) {
+ // fcgi_exit(handler);
+ //}
+}
+
+int fcgi_exit(struct fcgi_handler *handler)
+{
+ /* Always disable any backend notification first */
+ if (handler->server_fd > 0) {
+ mk_api->ev_del(mk_api->sched_loop(), &handler->event);
+ close(handler->server_fd);
+ handler->server_fd = -1;
+ }
+
+ /*
+ * Before to exit our handler, we need to verify that our parent
+ * channel have sent the whole information, otherwise we may face
+ * some corruption. If there is still some data enqueued, just
+ * defer the exit process.
+ */
+ if (mk_channel_is_empty(handler->cs->channel) != 0 &&
+ handler->eof == MK_FALSE &&
+ handler->active == MK_TRUE) {
+ MK_TRACE("[fastcgi=%i] deferring exit, EOF stream",
+ handler->server_fd);
+
+ /* Now set an EOF stream/callback to resume the exiting process */
+ mk_stream_in_eof(handler->stream,
+ NULL,
+ fcgi_stream_eof);
+ handler->eof = MK_TRUE;
+ return 1;
+ }
+
+ MK_TRACE("[fastcgi] exiting");
+
+ if (handler->iov) {
+ mk_api->iov_free(handler->iov);
+ mk_api->sched_event_free((struct mk_event *) handler);
+ handler->iov = NULL;
+ }
+
+ if (handler->active == MK_TRUE) {
+ handler->active = MK_FALSE;
+ mk_api->http_request_end(handler->plugin, handler->cs, handler->hangup);
+ }
+
+ return 1;
+}
+
+int fcgi_error(struct fcgi_handler *handler)
+{
+ fcgi_exit(handler);
+ mk_api->http_request_error(500, handler->cs, handler->sr, handler->plugin);
+ return 0;
+}
+
+static int fcgi_response(struct fcgi_handler *handler, char *buf, size_t len)
+{
+ int status;
+ int diff;
+ int xlen;
+ char tmp[16];
+ char *p;
+ char *end;
+ size_t p_len;
+ unsigned char advance;
+
+ MK_TRACE("[fastcgi=%i] process response len=%lu",
+ handler->server_fd, len);
+
+ p = buf;
+ p_len = len;
+
+ if (len == 0 && handler->chunked && handler->headers_set == MK_TRUE) {
+ MK_TRACE("[fastcgi=%i] sending EOF", handler->server_fd);
+ mk_stream_in_raw(handler->stream,
+ NULL,
+ "0\r\n\r\n", 5,
+ NULL, NULL);
+ mk_api->channel_flush(handler->cs->channel);
+ return 0;
+ }
+
+ if (handler->headers_set == MK_FALSE) {
+ advance = 4;
+
+ if (!buf) {
+ return -1;
+ }
+
+ end = getearliestbreak(buf, len, &advance);
+ if (!end) {
+ /* we need more data */
+ return -1;
+ }
+
+ handler->sr->headers.cgi = MK_TRUE;
+ if (strncasecmp(buf, "Status: ", 8) == 0) {
+ sscanf(buf + 8, "%d", &status);
+ MK_TRACE("FastCGI status %i", status);
+ mk_api->header_set_http_status(handler->sr, status);
+ }
+ else {
+ mk_api->header_set_http_status(handler->sr, 200);
+ }
+
+ /* Set transfer encoding */
+ if (handler->sr->protocol >= MK_HTTP_PROTOCOL_11) {
+ handler->sr->headers.transfer_encoding = MK_HEADER_TE_TYPE_CHUNKED;
+ handler->chunked = MK_TRUE;
+ }
+
+ mk_api->header_prepare(handler->plugin, handler->cs, handler->sr);
+
+ diff = (end - buf) + advance;
+ fcgi_write(handler, buf, diff);
+
+ p = buf + diff;
+ p_len -= diff;
+ handler->write_rounds++;
+ handler->headers_set = MK_TRUE;
+ }
+
+ if (p_len > 0) {
+ xlen = snprintf(tmp, 16, "%x\r\n", (unsigned int) p_len);
+ mk_stream_in_raw(handler->stream,
+ NULL,
+ tmp, xlen,
+ NULL, NULL);
+ fcgi_write(handler, p, p_len);
+ }
+
+ return 0;
+}
+
+int cb_fastcgi_on_read(void *data)
+{
+ int n;
+ int ret = 0;
+ int avail;
+ char *body;
+ size_t offset;
+ struct fcgi_handler *handler = data;
+ struct fcgi_record_header header;
+
+ if (handler->active == MK_FALSE) {
+ fcgi_exit(handler);
+ return -1;
+ }
+
+ avail = FCGI_BUF_SIZE - handler->buf_len;
+ n = read(handler->server_fd, handler->buf_data + handler->buf_len, avail);
+ MK_TRACE("[fastcgi=%i] read()=%i", handler->server_fd, n);
+ if (n <= 0) {
+ MK_TRACE("[fastcgi=%i] FastCGI server ended", handler->server_fd);
+ fcgi_exit(handler);
+ return -1;
+ }
+ else {
+ handler->buf_len += n;
+ }
+
+ if ((unsigned) handler->buf_len < FCGI_RECORD_HEADER_SIZE) {
+ /* wait for more data */
+ return n;
+ }
+
+ while (1) {
+ /* decode the header */
+ fcgi_read_header(&handler->buf_data, &header);
+
+ if (header.type != FCGI_STDOUT && header.type != FCGI_STDERR &&
+ header.type != FCGI_END_REQUEST) {
+ fcgi_exit(handler);
+ return -1;
+ }
+
+ /* Check if the package is complete */
+ if (handler->buf_len < (FCGI_RECORD_HEADER_SIZE + header.content_length)) {
+ /* we need more data */
+ return n;
+ }
+
+ body = handler->buf_data + FCGI_RECORD_HEADER_SIZE;
+ switch (header.type) {
+ case FCGI_STDOUT:
+ MK_TRACE("[fastcgi=%i] FCGI_STDOUT content_length=%i",
+ handler->server_fd, header.content_length);
+ /*
+ * Issue seen with Chrome & Firefox browsers:
+ * Sometimes content length is coming as ZERO and we are encoding a
+ * HTTP response packet with ZERO size data. This makes Chrome & Firefox
+ * browsers fail to proceed furhter and subsequent content loading fails.
+ * However, IE/Safari discards the packets with ZERO size data.
+ */
+ if (0 != header.content_length) {
+ ret = fcgi_response(handler, body, header.content_length);
+ }
+ else {
+ MK_TRACE("[fastcgi=%i] ZERO byte content length in FCGI_STDOUT, discard!!",
+ handler->server_fd);
+ ret = 0;
+ }
+ break;
+ case FCGI_STDERR:
+ MK_TRACE("[fastcgi=%i] FCGI_STDERR content_length=%i",
+ handler->server_fd, header.content_length);
+ break;
+ case FCGI_END_REQUEST:
+ MK_TRACE("[fastcgi=%i] FCGI_END_REQUEST content_length=%i",
+ handler->server_fd, header.content_length);
+ ret = fcgi_response(handler, NULL, 0);
+ break;
+ default:
+ //fcgi_exit(handler);
+ return -1;
+ }
+
+ if (ret == -1) {
+ /* Missing header breaklines ? */
+ return n;
+ }
+
+ /* adjust buffer content */
+ offset = FCGI_RECORD_HEADER_SIZE +
+ header.content_length + header.padding_length;
+
+ fcgi_buffer_consume(handler, offset);
+ }
+ return n;
+}
+
+int cb_fastcgi_request_flush(void *data)
+{
+ int ret;
+ size_t count = 0;
+ struct fcgi_handler *handler = data;
+
+ ret = mk_api->channel_write(&handler->fcgi_channel, &count);
+
+ MK_TRACE("[fastcgi=%i] %lu bytes, ret=%i",
+ handler->server_fd, count, ret);
+
+ if (ret == MK_CHANNEL_DONE || ret == MK_CHANNEL_EMPTY) {
+ /* Do we have more data for the stdin ? */
+ if (handler->stdin_length - handler->stdin_offset > 0) {
+ mk_api->iov_free(handler->iov);
+ handler->iov = mk_api->iov_create(64, 0);
+ fcgi_stdin_chunk(handler);
+
+ mk_api->stream_set(&handler->fcgi_stream,
+ MK_STREAM_IOV,
+ &handler->fcgi_channel,
+ handler->iov,
+ -1,
+ handler,
+ NULL, NULL, NULL);
+ return MK_CHANNEL_FLUSH;
+ }
+
+ /* Request done, switch the event side to receive the FCGI response */
+ handler->buf_len = 0;
+ handler->event.handler = cb_fastcgi_on_read;
+ ret = mk_api->ev_add(mk_api->sched_loop(),
+ handler->server_fd,
+ MK_EVENT_CUSTOM, MK_EVENT_READ, handler);
+ if (ret == -1) {
+ goto error;
+ }
+ }
+ else if (ret == MK_CHANNEL_ERROR) {
+ fcgi_exit(handler);
+ }
+ else if (ret == MK_CHANNEL_BUSY) {
+ return -1;
+ }
+
+ return ret;
+
+ error:
+ return -1;
+}
+
+/* Callback: on connect to the backend server */
+static int fastcgi_on_connect(struct fcgi_handler *handler)
+{
+ int ret;
+ int s_err;
+ size_t count;
+ socklen_t s_len = sizeof(s_err);
+ struct mk_list *head;
+ struct mk_plugin *pio;
+ struct mk_channel *channel;
+
+ /* Convert the original request to FCGI format */
+ ret = fcgi_encode_request(handler);
+ if (ret == -1) {
+ goto error;
+ }
+
+ /* Prepare the channel */
+ channel = &handler->fcgi_channel;
+ channel->type = MK_CHANNEL_SOCKET;
+ channel->fd = handler->server_fd;
+
+ /* FIXME: Discovery process needs to be fast */
+ mk_list_foreach(head, &mk_api->config->plugins) {
+ pio = mk_list_entry(head, struct mk_plugin, _head);
+ if (strncmp(pio->shortname, "liana", 5) == 0) {
+ break;
+ }
+ pio = NULL;
+ }
+ channel->io = pio->network;
+
+ mk_list_init(&channel->streams);
+ mk_api->stream_set(&handler->fcgi_stream,
+ MK_STREAM_IOV,
+ &handler->fcgi_channel,
+ handler->iov,
+ -1,
+ handler,
+ NULL, NULL, NULL);
+
+ handler->event.handler = cb_fastcgi_request_flush;
+ handler->event.data = handler;
+
+ return 0;
+
+ error:
+ fcgi_error(handler);
+ mk_api->channel_write(handler->cs->channel, &count);
+ return 0;
+}
+
+struct fcgi_handler *fcgi_handler_new(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr)
+{
+ int ret;
+ int entries;
+ struct fcgi_handler *h = NULL;
+ struct mk_net_connection *conn = NULL;
+
+ /* Allocate handler instance and set fields */
+ h = mk_api->mem_alloc_z(sizeof(struct fcgi_handler));
+ if (!h) {
+ return NULL;
+ }
+
+ stream = mk_stream_set(NULL, cs->channel, h,
+ NULL, NULL, NULL);
+ if (!stream) {
+ mk_api->mem_free(h);
+ return NULL;
+ }
+
+ h->stream = stream;
+ h->plugin = plugin;
+ h->cs = cs;
+ h->sr = sr;
+ h->write_rounds = 0;
+ h->active = MK_TRUE;
+ h->server_fd = -1;
+ h->eof = MK_FALSE;
+ h->stdin_length = 0;
+ h->stdin_offset = 0;
+ h->stdin_buffer = NULL;
+ h->conn = NULL;
+
+ /* Allocate enough space for our data */
+ entries = 128 + (cs->parser.header_count * 3);
+ h->iov = mk_api->iov_create(entries, 0);
+
+ /* Associate the handler with the Session Request */
+ sr->handler_data = h;
+
+ if (sr->protocol >= MK_HTTP_PROTOCOL_11) {
+ h->hangup = MK_FALSE;
+ }
+ else {
+ h->hangup = MK_TRUE;
+ }
+
+ /* Params buffer set an offset to include the header */
+ h->buf_len = FCGI_RECORD_HEADER_SIZE;
+
+ /* Request and async connection to the server */
+ if (fcgi_conf.server_addr) {
+ conn = mk_api->net_conn_create(fcgi_conf.server_addr,
+ atoi(fcgi_conf.server_port));
+ if (!conn) {
+ goto error;
+ }
+ h->conn = conn;
+ h->server_fd = conn->fd;
+ }
+ else if (fcgi_conf.server_path) {
+ /* FIXME: unix socket connection NOT FUNCTIONAL for now */
+ h->server_fd = mk_api->socket_open(fcgi_conf.server_path, MK_TRUE);
+ }
+
+ if (h->server_fd == -1) {
+ goto error;
+ }
+
+ fastcgi_on_connect(h);
+ return h;
+
+ error:
+ mk_api->iov_free(h->iov);
+ mk_api->mem_free(h);
+ sr->handler_data = NULL;
+ mk_api->http_request_error(500, cs, sr, plugin);
+
+ return NULL;
+}
diff --git a/fluent-bit/lib/monkey/plugins/fastcgi/fcgi_handler.h b/fluent-bit/lib/monkey/plugins/fastcgi/fcgi_handler.h
new file mode 100644
index 000000000..a0083ac7d
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/fastcgi/fcgi_handler.h
@@ -0,0 +1,128 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#ifndef MK_FASTCGI_HANDLER_H
+#define MK_FASTCGI_HANDLER_H
+
+#include <monkey/mk_api.h>
+
+/*
+ * Based on the information provided by the FastCGI spec, we use the
+ * following adapted structures:
+ *
+ * http://www.fastcgi.com/drupal/node/6?q=node/22
+ */
+struct fcgi_record_header {
+ uint8_t version;
+ uint8_t type;
+ uint16_t request_id;
+ uint16_t content_length;
+ uint8_t padding_length;
+ uint8_t reserved;
+};
+
+struct fcgi_begin_request_body {
+ uint16_t role;
+ uint8_t flags;
+ uint8_t reserved[5];
+};
+
+struct fcgi_begin_request_record {
+ struct fcgi_record_header header;
+ struct fcgi_begin_request_body body;
+};
+
+#define FCGI_VERSION_1 1
+#define FCGI_RECORD_MAX_SIZE 65535
+#define FCGI_RECORD_HEADER_SIZE sizeof(struct fcgi_record_header)
+#define FCGI_BUF_SIZE FCGI_RECORD_MAX_SIZE + FCGI_RECORD_HEADER_SIZE
+#define FCGI_BEGIN_REQUEST_BODY_SIZE sizeof(struct fcgi_begin_request_body)
+#define FCGI_RESPONDER 1
+#define FCGI_AUTHORIZER 2
+#define FCGI_FILTER 3
+
+/*
+ * Values for type component of FCGI_Header
+ */
+#define FCGI_BEGIN_REQUEST 1
+#define FCGI_ABORT_REQUEST 2
+#define FCGI_END_REQUEST 3
+#define FCGI_PARAMS 4
+#define FCGI_STDIN 5
+#define FCGI_STDOUT 6
+#define FCGI_STDERR 7
+#define FCGI_DATA 8
+#define FCGI_GET_VALUES 9
+#define FCGI_GET_VALUES_RESULT 10
+
+/*
+ * FastCGI Handler context, it keeps information of states and other
+ * request/response references.
+ */
+struct fcgi_handler {
+ struct mk_event event; /* built-in event-loop data */
+
+ int server_fd; /* backend FastCGI server */
+ int chunked; /* chunked response ? */
+ int active; /* is this handler active ? */
+ int hangup; /* hangup connection once ready ? */
+ int headers_set; /* headers set ? */
+ int eof; /* exiting: MK_TRUE / MK_FALSE */
+
+ /* stdin data */
+ uint64_t stdin_length;
+ uint64_t stdin_offset;
+ char *stdin_buffer;
+
+ struct mk_http_session *cs; /* HTTP session context */
+ struct mk_http_request *sr; /* HTTP request context */
+
+ /* FastCGI */
+ struct fcgi_begin_request_record header_request;
+
+ uint64_t write_rounds;
+ unsigned int buf_len;
+ char buf_data[FCGI_BUF_SIZE];
+
+ /* Channel to stream request to the FCGI server */
+ struct mk_channel fcgi_channel;
+ struct mk_stream fcgi_stream;
+
+ struct mk_iov *iov;
+ struct mk_list _head;
+
+ /* TCP connection context */
+ struct mk_net_connection *conn;
+};
+
+static inline void fcgi_encode16(void *a, unsigned b)
+{
+ unsigned char *c = a;
+
+ c[0] = (unsigned char) (b >> 8);
+ c[1] = (unsigned char) b;
+}
+
+struct fcgi_handler *fcgi_handler_new(struct mk_plugin *plugin,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr);
+
+int fcgi_exit(struct fcgi_handler *handler);
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/liana/ABOUT b/fluent-bit/lib/monkey/plugins/liana/ABOUT
new file mode 100644
index 000000000..1c1407ba4
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/liana/ABOUT
@@ -0,0 +1,4 @@
+Liana Plugin
+============
+Liana gives network layer to Monkey so you can build your own
+network layer for Monkey.
diff --git a/fluent-bit/lib/monkey/plugins/liana/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/liana/CMakeLists.txt
new file mode 100644
index 000000000..1f3797dcd
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/liana/CMakeLists.txt
@@ -0,0 +1,5 @@
+set(src
+ liana.c
+)
+
+MONKEY_PLUGIN(liana "${src}")
diff --git a/fluent-bit/lib/monkey/plugins/liana/MANDATORY b/fluent-bit/lib/monkey/plugins/liana/MANDATORY
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/liana/MANDATORY
diff --git a/fluent-bit/lib/monkey/plugins/liana/Makefile.in b/fluent-bit/lib/monkey/plugins/liana/Makefile.in
new file mode 100644
index 000000000..ef9fd4fc3
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/liana/Makefile.in
@@ -0,0 +1,18 @@
+all: monkey-liana.so
+include ../Make.common
+
+CC = @echo " CC $(_PATH)/$@"; $CC
+CC_QUIET= @echo -n; $CC
+AR = @echo " AR $(_PATH)/$@"; $AR
+CFLAGS = $CFLAGS
+LDFLAGS = $LDFLAGS
+DEFS = $DEFS
+LIANA_OBJECTS = liana.o
+
+-include $(LIANA_OBJECTS:.o=.d)
+
+monkey-liana.so: $(LIANA_OBJECTS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $(DEFS) -shared -o $@ $^ -lc
+
+monkey-liana.a: $(LIANA_OBJECTS)
+ $(AR) rcs $@ $(LIANA_OBJECTS)
diff --git a/fluent-bit/lib/monkey/plugins/liana/README b/fluent-bit/lib/monkey/plugins/liana/README
new file mode 100644
index 000000000..69a4760f9
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/liana/README
@@ -0,0 +1,7 @@
+Liana Networking Plugin
+=======================
+
+Liana is the base network plugin for Monkey, it provides the
+most basic socket calls such as: accept(), read(), write() etc.
+
+Without Liana do not expect a default HTTP Server working :)
diff --git a/fluent-bit/lib/monkey/plugins/liana/STATIC b/fluent-bit/lib/monkey/plugins/liana/STATIC
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/liana/STATIC
diff --git a/fluent-bit/lib/monkey/plugins/liana/liana.c b/fluent-bit/lib/monkey/plugins/liana/liana.c
new file mode 100644
index 000000000..3bfe288b8
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/liana/liana.c
@@ -0,0 +1,221 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
+#if defined (__linux__)
+#include <sys/sendfile.h>
+#endif
+
+#include <monkey/mk_api.h>
+
+int mk_liana_plugin_init(struct mk_plugin *plugin, char *confdir)
+{
+ (void) confdir;
+ (void) plugin;
+
+ return 0;
+}
+
+int mk_liana_plugin_exit(struct mk_plugin *plugin)
+{
+ (void) plugin;
+
+ return 0;
+}
+
+int mk_liana_read(struct mk_plugin *plugin, int socket_fd, void *buf, int count)
+{
+ (void) plugin;
+
+ return recv(socket_fd, (void*)buf, count, 0);
+}
+
+int mk_liana_write(struct mk_plugin *plugin, int socket_fd, const void *buf, size_t count )
+{
+ ssize_t bytes_sent = -1;
+
+ (void) plugin;
+
+ bytes_sent = send(socket_fd, buf, count, 0);
+
+ return bytes_sent;
+}
+
+int mk_liana_writev(struct mk_plugin *plugin, int socket_fd, struct mk_iov *mk_io)
+{
+ ssize_t bytes_sent = -1;
+
+ (void) plugin;
+
+ bytes_sent = plugin->api->iov_send(socket_fd, mk_io);
+
+ return bytes_sent;
+}
+
+int mk_liana_close(struct mk_plugin *plugin, int socket_fd)
+{
+ (void) plugin;
+
+#ifdef _WIN32
+ return closesocket(socket_fd);
+#else
+ return close(socket_fd);
+#endif
+}
+
+int mk_liana_send_file(struct mk_plugin *plugin, int socket_fd, int file_fd, off_t *file_offset,
+ size_t file_count)
+{
+ ssize_t ret = -1;
+
+ (void) plugin;
+
+#if defined (__linux__)
+ ret = sendfile(socket_fd, file_fd, file_offset, file_count);
+ if (ret == -1 && errno != EAGAIN) {
+ PLUGIN_TRACE("[FD %i] error from sendfile(): %s",
+ socket_fd, strerror(errno));
+ }
+ return ret;
+#elif defined (__APPLE__)
+ off_t offset = *file_offset;
+ off_t len = (off_t) file_count;
+
+ ret = sendfile(file_fd, socket_fd, offset, &len, NULL, 0);
+ if (ret == -1 && errno != EAGAIN) {
+ PLUGIN_TRACE("[FD %i] error from sendfile(): %s",
+ socket_fd, strerror(errno));
+ }
+ else if (len > 0) {
+ *file_offset += len;
+ return len;
+ }
+ return ret;
+#elif defined (__FreeBSD__)
+ off_t offset = *file_offset;
+ off_t len = (off_t) file_count;
+
+ ret = sendfile(file_fd, socket_fd, offset, len, NULL, 0, 0);
+ if (ret == -1 && errno != EAGAIN) {
+ PLUGIN_TRACE("[FD %i] error from sendfile(): %s",
+ socket_fd, strerror(errno));
+ }
+ else if (len > 0) {
+ *file_offset += len;
+ return len;
+ }
+ return ret;
+#else
+ #pragma message ("This is a terrible sendfile \"implementation\" and just a crutch")
+
+ ssize_t bytes_written = 0;
+ ssize_t to_be_sent = -1;
+ ssize_t send_ret = -1;
+ uint8_t temporary_buffer[1024];
+
+ if (NULL != file_offset) {
+ lseek(file_fd, *file_offset, SEEK_SET);
+ }
+
+ while (1) {
+ memset(temporary_buffer, 0, sizeof(temporary_buffer));
+
+ ret = read(file_fd, temporary_buffer, sizeof(temporary_buffer));
+
+ if (0 == ret)
+ {
+ return bytes_written;
+ }
+ else if (0 > ret)
+ {
+ return -1;
+ }
+ else if (0 < ret)
+ {
+ to_be_sent = ret;
+
+ while (to_be_sent > 0)
+ {
+ send_ret = send(file_fd, &temporary_buffer[ret - to_be_sent], to_be_sent, 0);
+
+ if (-1 == send_ret)
+ {
+ if (EAGAIN != errno &&
+ EWOULDBLOCK != errno)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ bytes_written += send_ret;
+ to_be_sent -= send_ret;
+ }
+ }
+ }
+ }
+#endif
+}
+
+/* Network Layer plugin Callbacks */
+struct mk_plugin_network mk_plugin_network_liana = {
+ .read = mk_liana_read,
+ .write = mk_liana_write,
+ .writev = mk_liana_writev,
+ .close = mk_liana_close,
+ .send_file = mk_liana_send_file,
+ .buffer_size = MK_REQUEST_CHUNK
+};
+
+struct mk_plugin mk_plugin_liana = {
+ /* Identification */
+ .shortname = "liana",
+ .name = "Liana Network Layer",
+ .version = MK_VERSION_STR,
+ .hooks = MK_PLUGIN_NETWORK_LAYER,
+
+ /* Init / Exit */
+ .init_plugin = mk_liana_plugin_init,
+ .exit_plugin = mk_liana_plugin_exit,
+
+ /* Init Levels */
+ .master_init = NULL,
+ .worker_init = NULL,
+
+ /* Type */
+ .network = &mk_plugin_network_liana,
+
+ /* Capabilities */
+ .capabilities = MK_CAP_SOCK_PLAIN
+};
diff --git a/fluent-bit/lib/monkey/plugins/logger/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/logger/CMakeLists.txt
new file mode 100644
index 000000000..156a0847f
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/logger/CMakeLists.txt
@@ -0,0 +1,21 @@
+set(src
+ pointers.c
+ logger.c
+ )
+
+# Validate splice()
+check_c_source_compiles("
+ #define _GNU_SOURCE
+ #include <stdio.h>
+ #include <fcntl.h>
+ int main() {
+ return splice(0, NULL, 1,
+ NULL, 1, SPLICE_F_MOVE);
+ }" HAVE_SPLICE)
+
+if (HAVE_SPLICE)
+ MK_DEFINITION(MK_HAVE_SPLICE)
+endif()
+
+MONKEY_PLUGIN(logger "${src}")
+add_subdirectory(conf)
diff --git a/fluent-bit/lib/monkey/plugins/logger/conf/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/logger/conf/CMakeLists.txt
new file mode 100644
index 000000000..2c4e9b069
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/logger/conf/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(conf_dir "${MK_PATH_CONF}/plugins/logger/")
+
+install(DIRECTORY DESTINATION ${conf_dir})
+configure_file(
+ "${PROJECT_SOURCE_DIR}/plugins/logger/conf/logger.conf.in"
+ "${PROJECT_BINARY_DIR}/conf/plugins/logger/logger.conf"
+ )
+
+if(NOT BUILD_LOCAL)
+ install(FILES ${PROJECT_BINARY_DIR}/conf/plugins/logger/logger.conf DESTINATION ${conf_dir})
+endif()
diff --git a/fluent-bit/lib/monkey/plugins/logger/conf/logger.conf.in b/fluent-bit/lib/monkey/plugins/logger/conf/logger.conf.in
new file mode 100644
index 000000000..5e06a8d6c
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/logger/conf/logger.conf.in
@@ -0,0 +1,22 @@
+# Logger:
+# -------
+# This plugin allows the creation of log files for each request that arrives.
+# It uses an access and error file which are defined inside every virtual
+# host file, this section set just global directives for the plugin.
+
+[LOGGER]
+ # FlushTimeout
+ # ------------
+ # This key define in seconds, the waiting time before to flush the
+ # data to the log file.
+ # Allowed values must be greater than zero (FlushTimeout > 0).
+
+ FlushTimeout 3
+
+ # MasterLog
+ # ---------
+ # This key define a master log file which is used when Monkey runs in daemon
+ # mode, so any Monkey output (stdout) will be redirected to the file
+ # specified here. The server port will be appended to the filename.
+
+ MasterLog @MK_PATH_LOG@/master.log
diff --git a/fluent-bit/lib/monkey/plugins/logger/logger.c b/fluent-bit/lib/monkey/plugins/logger/logger.c
new file mode 100644
index 000000000..4dfcfb001
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/logger/logger.c
@@ -0,0 +1,852 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <monkey/mk_api.h>
+
+/* System Headers */
+#include <time.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/* Local Headers */
+#include "logger.h"
+#include "pointers.h"
+
+struct status_response {
+ int i_status;
+ char *s_status;
+};
+
+static struct status_response response_codes[] = {
+ /* Common matches first */
+ {200, "200"}, {404, "404"},
+
+ {100, "100"}, {101, "101"},
+ {201, "201"}, {202, "202"}, {203, "203"}, {204, "204"},
+ {205, "205"}, {206, "206"},
+ {300, "300"}, {301, "301"}, {302, "302"}, {303, "303"}, {304, "304"},
+ {305, "305"},
+ {400, "400"}, {401, "401"}, {402, "402"}, {403, "403"},
+ {405, "405"}, {406, "406"}, {407, "407"}, {408, "408"}, {409, "409"},
+ {410, "410"}, {411, "411"}, {412, "412"}, {413, "413"}, {414, "414"},
+ {415, "415"},
+ {500, "500"}, {501, "501"}, {502, "502"}, {503, "503"}, {504, "504"},
+ {505, "505"},
+};
+
+static struct log_target *mk_logger_match_by_host(struct mk_vhost *host, int is_ok)
+{
+ struct mk_list *head;
+ struct log_target *entry;
+
+ mk_list_foreach(head, &targets_list) {
+ entry = mk_list_entry(head, struct log_target, _head);
+ if (entry->host == host && entry->is_ok == is_ok) {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+static struct iov *mk_logger_get_cache()
+{
+ return pthread_getspecific(cache_iov);
+}
+
+static ssize_t _mk_logger_append(int pipe_fd_in,
+ int file_fd_out,
+ size_t bytes)
+{
+ ssize_t ret;
+#ifdef MK_HAVE_SPLICE
+ ret = splice(pipe_fd_in, NULL, file_fd_out,
+ NULL, bytes, SPLICE_F_MOVE);
+ return ret;
+#else
+ unsigned char buffer[4096];
+ ssize_t buffer_used;
+ size_t bytes_written = 0;
+
+ while (bytes_written < bytes) {
+ ret = read(pipe_fd_in, buffer, sizeof(buffer));
+ if (ret < 0) {
+ break;
+ }
+ buffer_used = ret;
+ ret = write(file_fd_out, buffer, buffer_used);
+ if (ret < 0) {
+ break;
+ }
+ bytes_written += ret;
+ }
+ if (ret < 0 && bytes_written == 0)
+ return -1;
+ else
+ return bytes_written;
+#endif
+}
+
+static void mk_logger_start_worker(void *args)
+{
+ int fd;
+ int bytes, err;
+ int max_events = mk_api->config->nhosts;
+ int flog;
+ int clk;
+ long slen;
+ int timeout;
+ char *target;
+ (void) args;
+ struct mk_list *head;
+ struct log_target *entry;
+ struct mk_event *event;
+ struct mk_event_loop *evl;
+
+ /* pipe_size:
+ * ----------
+ * Linux set a pipe size usingto the PAGE_SIZE,
+ * check linux/include/pipe_fs_i.h for details:
+ *
+ * #define PIPE_SIZE PAGE_SIZE
+ *
+ * In the same header file we can found that every
+ * pipe has 16 pages, so our real memory allocation
+ * is: (PAGE_SIZE*PIPE_BUFFERS)
+ */
+ long pipe_size;
+
+ /* buffer_limit:
+ * -------------
+ * it means the maximum data that a monkey log pipe can contain.
+ */
+ long buffer_limit;
+
+ mk_api->worker_rename("monkey: logger");
+
+ /* Monkey allow just 75% of a pipe capacity */
+ pipe_size = sysconf(_SC_PAGESIZE) * 16;
+ buffer_limit = (pipe_size * MK_LOGGER_PIPE_LIMIT);
+
+ /* Creating poll */
+ evl = mk_api->ev_loop_create(max_events);
+
+ /* Registering targets for virtualhosts */
+ mk_list_foreach(head, &targets_list) {
+ entry = mk_list_entry(head, struct log_target, _head);
+ event = &entry->event;
+ event->mask = MK_EVENT_EMPTY;
+ event->data = entry;
+ event->handler = NULL;
+ event->status = MK_EVENT_NONE;
+
+ /* Add access log file */
+ if (entry->pipe[0] > 0) {
+ event->fd = entry->pipe[0];
+ mk_api->ev_add(evl, entry->pipe[0],
+ MK_EVENT_CONNECTION, MK_EVENT_READ, entry);
+ }
+ }
+
+ /* Set initial timeout */
+ timeout = time(NULL) + mk_logger_timeout;
+
+ /* Reading pipe buffer */
+ while (1) {
+ usleep(1200);
+
+ /* wait for events */
+ mk_api->ev_wait(evl);
+
+ /* get current time */
+ clk = mk_api->time_unix();
+
+ /* translate the backend events triggered */
+ mk_event_foreach(event, evl) {
+ entry = (struct log_target *) event;
+ target = entry->file;
+ fd = entry->pipe[0];
+
+ err = ioctl(fd, FIONREAD, &bytes);
+ if (mk_unlikely(err == -1)){
+ perror("ioctl");
+ }
+
+ if (bytes < buffer_limit && clk <= timeout) {
+ continue;
+ }
+
+ timeout = clk + mk_logger_timeout;
+
+ flog = open(target, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
+ if (mk_unlikely(flog == -1)) {
+ mk_warn("Could not open logfile '%s' (%s)", target, strerror(errno));
+
+ int consumed = 0;
+ char buf[255];
+ do {
+ slen = read(fd, buf, 255);
+ if (slen > 0) {
+ consumed += slen;
+ }
+ else {
+ break;
+ }
+ } while (consumed < bytes);
+
+ continue;
+ }
+
+ lseek(flog, 0, SEEK_END);
+ slen = _mk_logger_append(fd, flog, bytes);
+ if (mk_unlikely(slen == -1)) {
+ mk_warn("Could not write to log file: splice() = %ld", slen);
+ }
+
+ MK_TRACE("written %i bytes", bytes);
+ close(flog);
+ }
+ }
+}
+
+static int mk_logger_read_config(char *path)
+{
+ int timeout;
+ char *logfilename = NULL;
+ unsigned long len;
+ char *default_file = NULL;
+ struct mk_rconf *conf;
+ struct mk_rconf_section *section;
+
+ mk_api->str_build(&default_file, &len, "%slogger.conf", path);
+ conf = mk_api->config_open(default_file);
+ if (!conf) {
+ return -1;
+ }
+
+ section = mk_api->config_section_get(conf, "LOGGER");
+ if (section) {
+
+ /* FlushTimeout */
+ timeout = (size_t) mk_api->config_section_get_key(section,
+ "FlushTimeout",
+ MK_RCONF_NUM);
+ if (timeout <= 0) {
+ mk_err("FlushTimeout does not have a proper value");
+ exit(EXIT_FAILURE);
+ }
+ mk_logger_timeout = timeout;
+ MK_TRACE("FlushTimeout %i seconds", mk_logger_timeout);
+
+ /* MasterLog */
+ logfilename = mk_api->config_section_get_key(section,
+ "MasterLog",
+ MK_RCONF_STR);
+ if (logfilename == NULL) {
+ mk_err("MasterLog does not have a proper value");
+ exit(EXIT_FAILURE);
+ }
+
+ mk_logger_master_path = logfilename;
+ MK_TRACE("MasterLog '%s'", mk_logger_master_path);
+ }
+
+ mk_api->mem_free(default_file);
+ mk_api->config_free(conf);
+
+ return 0;
+}
+
+static void mk_logger_print_listeners()
+{
+ struct mk_list *head;
+ struct mk_config_listener *listener;
+
+ mk_list_foreach(head, &mk_api->config->listeners) {
+ listener = mk_list_entry(head, struct mk_config_listener, _head);
+ printf(" listen on %s:%s\n",
+ listener->address,
+ listener->port);
+ }
+}
+
+static void mk_logger_print_details(void)
+{
+ time_t now;
+ struct tm *current;
+
+ now = time(NULL);
+ current = localtime(&now);
+ printf("[%i/%02i/%02i %02i:%02i:%02i] Monkey Started\n",
+ current->tm_year + 1900,
+ current->tm_mon + 1,
+ current->tm_mday,
+ current->tm_hour,
+ current->tm_min,
+ current->tm_sec);
+ printf(" version : %s\n", MK_VERSION_STR);
+ printf(" number of workers: %i\n", mk_api->config->workers);
+ mk_logger_print_listeners();
+ fflush(stdout);
+}
+
+int mk_logger_plugin_init(struct plugin_api **api, char *confdir)
+{
+ int fd;
+ mk_api = *api;
+
+ /* Specific thread key */
+ pthread_key_create(&cache_iov, NULL);
+ pthread_key_create(&cache_content_length, NULL);
+ pthread_key_create(&cache_status, NULL);
+ pthread_key_create(&cache_ip_str, NULL);
+
+ /* Global configuration */
+ mk_logger_timeout = MK_LOGGER_TIMEOUT_DEFAULT;
+ mk_logger_master_path = NULL;
+ mk_logger_read_config(confdir);
+
+ /* Check masterlog */
+ if (mk_logger_master_path) {
+ fd = open(mk_logger_master_path, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
+ if (fd == -1) {
+ mk_err("Could not open/create master logfile %s", mk_logger_master_path);
+ exit(EXIT_FAILURE);
+
+ }
+ else {
+ /* Close test FD for MasterLog */
+ close(fd);
+ }
+ }
+
+ return 0;
+}
+
+int mk_logger_plugin_exit()
+{
+ struct mk_list *head, *tmp;
+ struct log_target *entry;
+
+ mk_list_foreach_safe(head, tmp, &targets_list) {
+ entry = mk_list_entry(head, struct log_target, _head);
+ mk_list_del(&entry->_head);
+ if (entry->pipe[0] > 0) close(entry->pipe[0]);
+ if (entry->pipe[1] > 0) close(entry->pipe[1]);
+ mk_api->mem_free(entry->file);
+ mk_api->mem_free(entry);
+ }
+
+ mk_api->mem_free(mk_logger_master_path);
+
+ return 0;
+}
+
+int mk_logger_master_init(struct mk_server_config *config)
+{
+ int ret;
+ struct log_target *new;
+ struct mk_vhost *entry_host;
+ struct mk_list *hosts = &mk_api->config->hosts;
+ struct mk_list *head_host;
+ struct mk_rconf_section *section;
+ char *access_file_name = NULL;
+ char *error_file_name = NULL;
+ pthread_t tid;
+ (void) config;
+
+ /* Restore STDOUT if we are in background mode */
+ if (mk_logger_master_path != NULL && mk_api->config->is_daemon == MK_TRUE) {
+ mk_logger_master_stdout = freopen(mk_logger_master_path, "ae", stdout);
+ mk_logger_master_stderr = freopen(mk_logger_master_path, "ae", stderr);
+ mk_logger_print_details();
+ }
+
+ MK_TRACE("Reading virtual hosts");
+
+ mk_list_init(&targets_list);
+
+ mk_list_foreach(head_host, hosts) {
+ entry_host = mk_list_entry(head_host, struct mk_vhost, _head);
+
+ /* Read logger section from virtual host configuration */
+ section = mk_api->config_section_get(entry_host->config, "LOGGER");
+ if (section) {
+ /* Read configuration entries */
+ access_file_name = (char *) mk_api->config_section_get_key(section,
+ "AccessLog",
+ MK_RCONF_STR);
+ error_file_name = (char *) mk_api->config_section_get_key(section,
+ "ErrorLog",
+ MK_RCONF_STR);
+
+ if (access_file_name) {
+ new = mk_api->mem_alloc(sizeof(struct log_target));
+ new->is_ok = MK_TRUE;
+
+ /* Set access pipe */
+ if (pipe(new->pipe) < 0) {
+ mk_err("Could not create pipe");
+ exit(EXIT_FAILURE);
+ }
+ if (fcntl(new->pipe[1], F_SETFL, O_NONBLOCK) == -1) {
+ perror("fcntl");
+ }
+ if (fcntl(new->pipe[0], F_SETFD, FD_CLOEXEC) == -1) {
+ perror("fcntl");
+ }
+ if (fcntl(new->pipe[1], F_SETFD, FD_CLOEXEC) == -1) {
+ perror("fcntl");
+ }
+ new->file = access_file_name;
+ new->host = entry_host;
+ mk_list_add(&new->_head, &targets_list);
+ }
+
+ /* Set error pipe */
+ if (error_file_name) {
+ new = mk_api->mem_alloc(sizeof(struct log_target));
+ new->is_ok = MK_FALSE;
+
+ if (pipe(new->pipe) < 0) {
+ mk_err("Could not create pipe");
+ exit(EXIT_FAILURE);
+ }
+ if (fcntl(new->pipe[1], F_SETFL, O_NONBLOCK) == -1) {
+ perror("fcntl");
+ }
+ if (fcntl(new->pipe[0], F_SETFD, FD_CLOEXEC) == -1) {
+ perror("fcntl");
+ }
+ if (fcntl(new->pipe[1], F_SETFD, FD_CLOEXEC) == -1 ){
+ perror("fcntl");
+ }
+ new->file = error_file_name;
+ new->host = entry_host;
+ mk_list_add(&new->_head, &targets_list);
+
+ }
+ }
+ }
+
+ ret = mk_api->worker_spawn((void *) mk_logger_start_worker, NULL, &tid);
+ if (ret == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void mk_logger_worker_init()
+{
+ struct mk_iov *iov_log;
+ mk_ptr_t *content_length;
+ mk_ptr_t *status;
+ mk_ptr_t *ip_str;
+
+
+ MK_TRACE("Creating thread cache");
+
+ /* Cache iov log struct */
+ iov_log = mk_api->iov_create(15, 0);
+ pthread_setspecific(cache_iov, (void *) iov_log);
+
+ /* Cache content length */
+ content_length = mk_api->mem_alloc_z(sizeof(mk_ptr_t));
+ content_length->data = mk_api->mem_alloc_z(MK_UTILS_INT2MKP_BUFFER_LEN);
+ content_length->len = -1;
+ pthread_setspecific(cache_content_length, (void *) content_length);
+
+ /* Cahe status */
+ status = mk_api->mem_alloc_z(sizeof(mk_ptr_t));
+ status->data = mk_api->mem_alloc_z(MK_UTILS_INT2MKP_BUFFER_LEN);
+ status->len = -1;
+ pthread_setspecific(cache_status, (void *) status);
+
+ /* Cache IP address */
+ ip_str = mk_api->mem_alloc_z(sizeof(mk_ptr_t));
+ ip_str->data = mk_api->mem_alloc_z(INET6_ADDRSTRLEN + 1);
+ ip_str->len = -1;
+ pthread_setspecific(cache_ip_str, (void *) ip_str);
+}
+
+int mk_logger_stage40(struct mk_http_session *cs, struct mk_http_request *sr)
+{
+ int i, http_status, ret, tmp;
+ int array_len = ARRAY_SIZE(response_codes);
+ int access;
+ struct log_target *target;
+ struct mk_iov *iov;
+ mk_ptr_t *date;
+ mk_ptr_t *content_length;
+ mk_ptr_t *ip_str;
+ mk_ptr_t status;
+
+ /* Set response status */
+ http_status = sr->headers.status;
+
+ if (http_status < 400) {
+ access = MK_TRUE;
+ }
+ else {
+ access = MK_FALSE;
+ }
+
+ /* Look for target log file */
+ target = mk_logger_match_by_host(sr->host_conf, access);
+ if (!target) {
+ MK_TRACE("No target found");
+ return 0;
+ }
+
+ /* Get iov cache struct and reset indexes */
+ iov = (struct mk_iov *) mk_logger_get_cache();
+ iov->iov_idx = 0;
+ iov->buf_idx = 0;
+ iov->total_len = 0;
+
+ /* Format IP string */
+ ip_str = pthread_getspecific(cache_ip_str);
+ ret = mk_api->socket_ip_str(cs->socket,
+ &ip_str->data,
+ INET6_ADDRSTRLEN + 1,
+ &ip_str->len);
+ /*
+ * If the socket is not longer available ip_str can be null,
+ * so we must check this condition and return
+ */
+ if (mk_unlikely(ret < 0)) {
+ return 0;
+ }
+
+ /* Add IP to IOV */
+ mk_api->iov_add(iov,
+ ip_str->data, ip_str->len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_dash.data,
+ mk_logger_iov_dash.len,
+ MK_FALSE);
+
+ /* Date/time when object was requested */
+ date = mk_api->time_human(cs->server);
+ mk_api->iov_add(iov,
+ date->data, date->len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+
+ /* Access Log */
+ if (http_status < 400) {
+ /* No access file defined */
+ if (!target->file) {
+ return 0;
+ }
+
+ /* HTTP Method */
+ mk_api->iov_add(iov,
+ sr->method_p.data,
+ sr->method_p.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+
+ /* HTTP URI required */
+ mk_api->iov_add(iov,
+ sr->uri.data, sr->uri.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+
+ /* HTTP Protocol */
+ mk_api->iov_add(iov,
+ sr->protocol_p.data, sr->protocol_p.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+
+ /* HTTP Status code response */
+ for (i=0; i < array_len; i++) {
+ if (response_codes[i].i_status == http_status) {
+ break;
+ }
+ }
+
+ if (array_len == i) {
+ mk_api->str_itop(http_status, &status);
+ status.len -= 2;
+ }
+ else {
+ status.data = response_codes[i].s_status;
+ status.len = 3;
+ }
+ mk_api->iov_add(iov,
+ status.data,
+ status.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+
+ /* Content Length */
+ if (sr->method != MK_METHOD_HEAD) {
+ /* Int to mk_ptr_t */
+ content_length = pthread_getspecific(cache_content_length);
+
+ tmp = sr->headers.content_length;
+ if (tmp < 0) {
+ tmp = 0;
+ }
+
+ mk_api->str_itop(tmp, content_length);
+
+ mk_api->iov_add(iov,
+ content_length->data, content_length->len - 2,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ }
+ else {
+ mk_api->iov_add(iov,
+ mk_logger_iov_empty.data,
+ mk_logger_iov_empty.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ }
+
+ /* Write iov array to pipe */
+ mk_api->iov_send(target->pipe[1], iov);
+ }
+ else {
+ if (mk_unlikely(!target->file)) {
+ return 0;
+ }
+
+ /* For unknown errors. Needs to exist until iov_send. */
+ char err_str[80];
+
+ switch (http_status) {
+ case MK_CLIENT_BAD_REQUEST:
+ mk_api->iov_add(iov,
+ error_msg_400.data,
+ error_msg_400.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ break;
+ case MK_CLIENT_FORBIDDEN:
+ mk_api->iov_add(iov,
+ error_msg_403.data,
+ error_msg_403.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ sr->uri.data,
+ sr->uri.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+
+ break;
+ case MK_CLIENT_NOT_FOUND:
+ mk_api->iov_add(iov,
+ error_msg_404.data,
+ error_msg_404.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+
+ mk_api->iov_add(iov,
+ sr->uri.data,
+ sr->uri.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+
+ break;
+ case MK_CLIENT_METHOD_NOT_ALLOWED:
+ mk_api->iov_add(iov,
+ error_msg_405.data,
+ error_msg_405.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ sr->method_p.data,
+ sr->method_p.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ break;
+ case MK_CLIENT_REQUEST_TIMEOUT:
+ mk_api->iov_add(iov,
+ error_msg_408.data,
+ error_msg_408.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ break;
+ case MK_CLIENT_LENGTH_REQUIRED:
+ mk_api->iov_add(iov,
+ error_msg_411.data,
+ error_msg_411.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ break;
+ case MK_CLIENT_REQUEST_ENTITY_TOO_LARGE:
+ mk_api->iov_add(iov,
+ error_msg_413.data,
+ error_msg_413.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ break;
+ case MK_SERVER_NOT_IMPLEMENTED:
+ mk_api->iov_add(iov,
+ error_msg_501.data,
+ error_msg_501.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ sr->method_p.data,
+ sr->method_p.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ break;
+ case MK_SERVER_INTERNAL_ERROR:
+ mk_api->iov_add(iov,
+ error_msg_500.data,
+ error_msg_500.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+ break;
+ case MK_SERVER_HTTP_VERSION_UNSUP:
+ mk_api->iov_add(iov,
+ error_msg_505.data,
+ error_msg_505.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ break;
+ default:
+ {
+ int len = snprintf(err_str, 80, "[error %u] (no description)", http_status);
+ err_str[79] = '\0';
+ if (len > 79) len = 79;
+
+ mk_api->iov_add(iov,
+ err_str,
+ len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_space.data,
+ mk_logger_iov_space.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ sr->uri.data,
+ sr->uri.len,
+ MK_FALSE);
+ mk_api->iov_add(iov,
+ mk_logger_iov_lf.data,
+ mk_logger_iov_lf.len,
+ MK_FALSE);
+ }
+ break;
+ }
+
+
+ /* Write iov array to pipe */
+ mk_api->iov_send(target->pipe[1], iov);
+ }
+
+ return 0;
+}
+
+struct mk_plugin_stage mk_plugin_stage_logger = {
+ .stage40 = &mk_logger_stage40
+};
+
+struct mk_plugin mk_plugin_logger = {
+ /* Identification */
+ .shortname = "logger",
+ .name = "Log Writer",
+ .version = MK_VERSION_STR,
+ .hooks = MK_PLUGIN_STAGE,
+
+ /* Init / Exit */
+ .init_plugin = mk_logger_plugin_init,
+ .exit_plugin = mk_logger_plugin_exit,
+
+ /* Init Levels */
+ .master_init = mk_logger_master_init,
+ .worker_init = mk_logger_worker_init,
+
+ /* Type */
+ .stage = &mk_plugin_stage_logger
+};
diff --git a/fluent-bit/lib/monkey/plugins/logger/logger.h b/fluent-bit/lib/monkey/plugins/logger/logger.h
new file mode 100644
index 000000000..6c9668bf4
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/logger/logger.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+/* s_log status */
+#ifndef MK_LOGGER_H
+#define MK_LOGGER_H
+
+#include <stdio.h>
+#include <monkey/mk_api.h>
+
+#define MK_LOGGER_PIPE_LIMIT 0.75
+#define MK_LOGGER_TIMEOUT_DEFAULT 3
+
+int mk_logger_timeout;
+
+/* MasterLog variables */
+char *mk_logger_master_path;
+FILE *mk_logger_master_stdout;
+FILE *mk_logger_master_stderr;
+
+pthread_key_t cache_content_length;
+pthread_key_t cache_status;
+pthread_key_t cache_ip_str;
+pthread_key_t cache_iov;
+
+struct log_target
+{
+ struct mk_event event;
+
+ /* Pipes */
+ int is_ok;
+ int pipe[2];
+ char *file;
+
+ struct mk_vhost *host;
+ struct mk_list _head;
+};
+
+struct mk_list targets_list;
+
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/logger/pointers.c b/fluent-bit/lib/monkey/plugins/logger/pointers.c
new file mode 100644
index 000000000..11c48906a
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/logger/pointers.c
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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 <monkey/mk_plugin.h>
+
+#include "logger.h"
+#include "pointers.h"
+
+const mk_ptr_t mk_logger_iov_none = mk_ptr_init("");
+
+/* Writter helpers */
+const mk_ptr_t mk_logger_iov_dash = mk_ptr_init(MK_LOGGER_IOV_DASH);
+const mk_ptr_t mk_logger_iov_space = mk_ptr_init(MK_IOV_SPACE);
+const mk_ptr_t mk_logger_iov_lf = mk_ptr_init(MK_IOV_LF);
+const mk_ptr_t mk_logger_iov_empty = mk_ptr_init(MK_LOGGER_IOV_EMPTY);
+
+/* Error messages */
+const mk_ptr_t error_msg_400 = mk_ptr_init(ERROR_MSG_400);
+const mk_ptr_t error_msg_403 = mk_ptr_init(ERROR_MSG_403);
+const mk_ptr_t error_msg_404 = mk_ptr_init(ERROR_MSG_404);
+const mk_ptr_t error_msg_405 = mk_ptr_init(ERROR_MSG_405);
+const mk_ptr_t error_msg_408 = mk_ptr_init(ERROR_MSG_408);
+const mk_ptr_t error_msg_411 = mk_ptr_init(ERROR_MSG_411);
+const mk_ptr_t error_msg_413 = mk_ptr_init(ERROR_MSG_413);
+const mk_ptr_t error_msg_500 = mk_ptr_init(ERROR_MSG_500);
+const mk_ptr_t error_msg_501 = mk_ptr_init(ERROR_MSG_501);
+const mk_ptr_t error_msg_505 = mk_ptr_init(ERROR_MSG_505);
diff --git a/fluent-bit/lib/monkey/plugins/logger/pointers.h b/fluent-bit/lib/monkey/plugins/logger/pointers.h
new file mode 100644
index 000000000..d353c2583
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/logger/pointers.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#ifndef MK_LOGGER_POINTERS_H
+#define MK_LOGGER_POINTERS_H
+
+#include <memory.h>
+
+/* Request error messages for log file */
+#define ERROR_MSG_400 "[error 400] Bad Request"
+#define ERROR_MSG_403 "[error 403] Forbidden"
+#define ERROR_MSG_404 "[error 404] Not Found"
+#define ERROR_MSG_405 "[error 405] Method Not Allowed"
+#define ERROR_MSG_408 "[error 408] Request Timeout"
+#define ERROR_MSG_411 "[error 411] Length Required"
+#define ERROR_MSG_413 "[error 413] Request Entity Too Large"
+#define ERROR_MSG_500 "[error 500] Internal Server Error"
+#define ERROR_MSG_501 "[error 501] Not Implemented"
+#define ERROR_MSG_505 "[error 505] HTTP Version Not Supported"
+
+#define MK_LOGGER_IOV_DASH " - "
+#define MK_LOGGER_IOV_SPACE " "
+#define MK_LOGGER_IOV_EMPTY "-"
+
+/* mk pointers for errors */
+extern const mk_ptr_t error_msg_400;
+extern const mk_ptr_t error_msg_403;
+extern const mk_ptr_t error_msg_404;
+extern const mk_ptr_t error_msg_405;
+extern const mk_ptr_t error_msg_408;
+extern const mk_ptr_t error_msg_411;
+extern const mk_ptr_t error_msg_413;
+extern const mk_ptr_t error_msg_500;
+extern const mk_ptr_t error_msg_501;
+extern const mk_ptr_t error_msg_505;
+
+/* mk pointer for IOV */
+extern const mk_ptr_t mk_logger_iov_dash;
+extern const mk_ptr_t mk_logger_iov_space;
+extern const mk_ptr_t mk_logger_iov_lf;
+extern const mk_ptr_t mk_logger_iov_empty;
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/mandril/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/mandril/CMakeLists.txt
new file mode 100644
index 000000000..ba2e95717
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/mandril/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(src
+ mandril.c
+ )
+
+MONKEY_PLUGIN(mandril "${src}")
+add_subdirectory(conf)
diff --git a/fluent-bit/lib/monkey/plugins/mandril/conf/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/mandril/conf/CMakeLists.txt
new file mode 100644
index 000000000..44e95175c
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/mandril/conf/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(conf_dir "${MK_PATH_CONF}/plugins/mandril/")
+
+install(DIRECTORY DESTINATION ${conf_dir})
+
+if(BUILD_LOCAL)
+ file(COPY mandril.conf DESTINATION ${conf_dir})
+else()
+ install(FILES mandril.conf DESTINATION ${conf_dir})
+endif()
diff --git a/fluent-bit/lib/monkey/plugins/mandril/conf/mandril.conf b/fluent-bit/lib/monkey/plugins/mandril/conf/mandril.conf
new file mode 100644
index 000000000..efeaf5d9a
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/mandril/conf/mandril.conf
@@ -0,0 +1,57 @@
+# Monkey HTTP Daemon - Mandril
+# ============================
+# Mandril Plugin provide security rules to be applied to the incomming
+# connections. If the client is rejected by some rule, it will get the
+# 403 Forbidden error status.
+#
+# It supports two restriction modes, by request URI and by IP (or network
+# range), make sure all your rules are defined under the section [RULES]:
+#
+# a) Restriction by request URI:
+#
+# You can define multiple keywords to restrict a specific incoming
+# request which hold that string. Check this example:
+#
+# [RULES]
+# URL documents
+# URL pictures
+# URL /private
+#
+# b) Restriction by IP or network range:
+#
+# Multiple rules can be defined to deny the access to specific incoming
+# clients:
+#
+# [RULES]
+# IP 10.20.1.1/24
+# IP 192.168.3.150
+#
+# In the first rule we are blocking a range of IPs from 10.20.1.1 to
+# 10.20.1.255. In the second example just one specific IP address.
+#
+# It also supports denying hotlinking from other domains.
+#
+# c)
+#
+# [RULES]
+# deny_hotlink /imgs
+#
+# This rule will prevent access to all files under /imgs if the
+# request's Referer header is not from the same domain or its
+# subdomains.
+# If the Referer header is missing, the request will be accepted.
+#
+# You can mix the rules type under the [RULE] section, so the following example
+# is totally valid:
+#
+# [RULES]
+# URL documents
+# URL pictures
+# URL /private
+# IP 10.20.1.1/24
+# IP 192.168.3.150
+#
+
+[RULES]
+ # IP 127.0.0.1
+ # URL /imgs
diff --git a/fluent-bit/lib/monkey/plugins/mandril/mandril.c b/fluent-bit/lib/monkey/plugins/mandril/mandril.c
new file mode 100644
index 000000000..e8a988d08
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/mandril/mandril.c
@@ -0,0 +1,403 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright 2012, Sonny Karlsson
+ *
+ * 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.
+ */
+
+/* Monkey API */
+#include <monkey/mk_api.h>
+
+
+/* system */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+
+#include "mandril.h"
+
+static struct mk_rconf *conf;
+
+/* Read database configuration parameters */
+static int mk_security_conf(struct mk_plugin *plugin, char *confdir)
+{
+ int n;
+ int ret = 0;
+ unsigned long len;
+ char *conf_path = NULL;
+ char *_net, *_mask;
+
+ struct mk_secure_ip_t *new_ip;
+ struct mk_secure_url_t *new_url;
+ struct mk_secure_deny_hotlink_t *new_deny_hotlink;
+
+ struct mk_rconf_section *section;
+ struct mk_rconf_entry *entry;
+ struct mk_list *head;
+
+ /* Read configuration */
+ plugin->api->str_build(&conf_path, &len, "%s/mandril.conf", confdir);
+ conf = plugin->api->config_open(conf_path);
+ if (!conf) {
+ return -1;
+ }
+
+ section = plugin->api->config_section_get(conf, "RULES");
+ if (!section) {
+ return -1;
+ }
+
+ mk_list_foreach(head, &section->entries) {
+ entry = mk_list_entry(head, struct mk_rconf_entry, _head);
+
+ /* Passing to internal struct */
+ if (strcasecmp(entry->key, "IP") == 0) {
+ new_ip = plugin->api->mem_alloc(sizeof(struct mk_secure_ip_t));
+ n = plugin->api->str_search(entry->val, "/", 1);
+
+ /* subnet */
+ if (n > 0) {
+ /* split network addr and netmask */
+ _net = plugin->api->str_copy_substr(entry->val, 0, n);
+ _mask = plugin->api->str_copy_substr(entry->val,
+ n + 1,
+ strlen(entry->val));
+
+ /* validations... */
+ if (!_net || !_mask) {
+ mk_warn_ex(plugin->api,
+ "Mandril: cannot parse entry '%s' in RULES section",
+ entry->val);
+ goto ip_next;
+ }
+
+ /* convert ip string to network address */
+ if (inet_aton(_net, &new_ip->ip) == 0) {
+ mk_warn_ex(plugin->api,
+ "Mandril: invalid ip address '%s' in RULES section",
+ entry->val);
+ goto ip_next;
+ }
+
+ /* parse mask */
+ new_ip->netmask = strtol(_mask, (char **) NULL, 10);
+ if (new_ip->netmask <= 0 || new_ip->netmask >= 32) {
+ mk_warn_ex(plugin->api,
+ "Mandril: invalid mask value '%s' in RULES section",
+ entry->val);
+ goto ip_next;
+ }
+
+ /* complete struct data */
+ new_ip->is_subnet = MK_TRUE;
+ new_ip->network = MK_NET_NETWORK(new_ip->ip.s_addr, new_ip->netmask);
+ new_ip->hostmin = MK_NET_HOSTMIN(new_ip->ip.s_addr, new_ip->netmask);
+ new_ip->hostmax = MK_NET_HOSTMAX(new_ip->ip.s_addr, new_ip->netmask);
+
+ /* link node with main list */
+ mk_list_add(&new_ip->_head, &mk_secure_ip);
+
+ /*
+ * I know, you were instructed to hate 'goto' statements!, ok, show this
+ * code to your teacher and let him blame :P
+ */
+ ip_next:
+ if (_net) {
+ plugin->api->mem_free(_net);
+ }
+ if (_mask) {
+ plugin->api->mem_free(_mask);
+ }
+ }
+ else { /* normal IP address */
+
+ /* convert ip string to network address */
+ if (inet_aton(entry->val, &new_ip->ip) == 0) {
+ mk_warn_ex(plugin->api,
+ "Mandril: invalid ip address '%s' in RULES section",
+ entry->val);
+ }
+ else {
+ new_ip->is_subnet = MK_FALSE;
+ mk_list_add(&new_ip->_head, &mk_secure_ip);
+ }
+ }
+ }
+ else if (strcasecmp(entry->key, "URL") == 0) {
+ /* simple allcotion and data association */
+ new_url = plugin->api->mem_alloc(sizeof(struct mk_secure_url_t));
+ new_url->criteria = entry->val;
+
+ /* link node with main list */
+ mk_list_add(&new_url->_head, &mk_secure_url);
+ }
+ else if (strcasecmp(entry->key, "deny_hotlink") == 0) {
+ new_deny_hotlink = plugin->api->mem_alloc(sizeof(*new_deny_hotlink));
+ new_deny_hotlink->criteria = entry->val;
+
+ mk_list_add(&new_deny_hotlink->_head, &mk_secure_deny_hotlink);
+ }
+ }
+
+ plugin->api->mem_free(conf_path);
+
+ return ret;
+}
+
+static int mk_security_check_ip(int socket)
+{
+ int network;
+ struct mk_secure_ip_t *entry;
+ struct mk_list *head;
+ struct in_addr *addr;
+ struct sockaddr_in addr_t = {0};
+ socklen_t len = sizeof(addr_t);
+
+ if (getpeername(socket, (struct sockaddr *) &addr_t, &len) != 0) {
+ perror("getpeername");
+ return -1;
+ }
+
+ addr = &(addr_t).sin_addr;
+
+ PLUGIN_TRACE("[FD %i] Mandril validating IP address", socket);
+ mk_list_foreach(head, &mk_secure_ip) {
+ entry = mk_list_entry(head, struct mk_secure_ip_t, _head);
+
+ if (entry->is_subnet == MK_TRUE) {
+ /* Validate network */
+ network = MK_NET_NETWORK(addr->s_addr, entry->netmask);
+ if (network != entry->network) {
+ continue;
+ }
+ /* Validate host range */
+ if (addr->s_addr <= entry->hostmax && addr->s_addr >= entry->hostmin) {
+ PLUGIN_TRACE("[FD %i] Mandril closing by rule in ranges", socket);
+ return -1;
+ }
+ }
+ else {
+ if (addr->s_addr == entry->ip.s_addr) {
+ PLUGIN_TRACE("[FD %i] Mandril closing by rule in IP match", socket);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Check if the incoming URL is restricted for some rule */
+static int mk_security_check_url(struct mk_plugin *plugin, mk_ptr_t url)
+{
+ int n;
+ struct mk_list *head;
+ struct mk_secure_url_t *entry;
+
+ mk_list_foreach(head, &mk_secure_url) {
+ entry = mk_list_entry(head, struct mk_secure_url_t, _head);
+ n = plugin->api->str_search_n(url.data, entry->criteria, MK_STR_INSENSITIVE, url.len);
+ if (n >= 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+mk_ptr_t parse_referer_host(struct mk_http_header *header)
+{
+ unsigned int i, beginHost, endHost;
+ mk_ptr_t host;
+
+ host.data = NULL;
+ host.len = 0;
+
+ // Find end of "protocol://"
+ for (i = 0; i < header->val.len && !(header->val.data[i] == '/' && header->val.data[i+1] == '/'); i++);
+ if (i == header->val.len) {
+ goto error;
+ }
+ beginHost = i + 2;
+
+ // Find end of any "user:password@"
+ for (; i < header->val.len && header->val.data[i] != '@'; i++);
+ if (i < header->val.len) {
+ beginHost = i + 1;
+ }
+
+ // Find end of "host", (beginning of :port or /path)
+ for (i = beginHost; i < header->val.len && header->val.data[i] != ':' && header->val.data[i] != '/'; i++);
+ endHost = i;
+
+ host.data = header->val.data + beginHost;
+ host.len = endHost - beginHost;
+ return host;
+error:
+ host.data = NULL;
+ host.len = 0;
+ return host;
+}
+
+static int mk_security_check_hotlink(struct mk_plugin *plugin,
+ mk_ptr_t url, mk_ptr_t host,
+ struct mk_http_header *referer)
+{
+ mk_ptr_t ref_host = parse_referer_host(referer);
+ unsigned int domains_matched = 0;
+ int i = 0;
+ const char *curA, *curB;
+ struct mk_list *head;
+ struct mk_secure_deny_hotlink_t *entry;
+
+ if (ref_host.data == NULL) {
+ return 0;
+ }
+ else if (host.data == NULL) {
+ mk_err_ex(plugin->api, "No host data.");
+ return -1;
+ }
+
+ mk_list_foreach(head, &mk_secure_url) {
+ entry = mk_list_entry(head, struct mk_secure_deny_hotlink_t, _head);
+ i = plugin->api->str_search_n(url.data, entry->criteria, MK_STR_INSENSITIVE, url.len);
+ if (i >= 0) {
+ break;
+ }
+ }
+ if (i < 0) {
+ return 0;
+ }
+
+ curA = host.data + host.len;
+ curB = ref_host.data + ref_host.len;
+
+ // Match backwards from root domain.
+ while (curA > host.data && curB > ref_host.data) {
+ i++;
+ curA--;
+ curB--;
+
+ if ((*curA == '.' && *curB == '.') ||
+ curA == host.data || curB == ref_host.data) {
+ if (i < 1) {
+ break;
+ }
+ else if (curA == host.data &&
+ !(curB == ref_host.data || *(curB - 1) == '.')) {
+ break;
+ }
+ else if (curB == ref_host.data &&
+ !(curA == host.data || *(curA - 1) == '.')) {
+ break;
+ }
+ else if (strncasecmp(curA, curB, i)) {
+ break;
+ }
+ domains_matched += 1;
+ i = 0;
+ }
+ }
+
+ // Block connection if none or only top domain matched.
+ return domains_matched >= 2 ? 0 : -1;
+}
+
+int mk_mandril_plugin_init(struct mk_plugin *plugin, char *confdir)
+{
+ /* Init security lists */
+ mk_list_init(&mk_secure_ip);
+ mk_list_init(&mk_secure_url);
+ mk_list_init(&mk_secure_deny_hotlink);
+
+ /* Read configuration */
+ mk_security_conf(plugin, confdir);
+
+ return 0;
+}
+
+int mk_mandril_plugin_exit()
+{
+ return 0;
+}
+
+int mk_mandril_stage10(int socket)
+{
+ /* Validate ip address with Mandril rules */
+ if (mk_security_check_ip(socket) != 0) {
+ PLUGIN_TRACE("[FD %i] Mandril close connection", socket);
+ return MK_PLUGIN_RET_CLOSE_CONX;
+ }
+
+ return MK_PLUGIN_RET_CONTINUE;
+}
+
+int mk_mandril_stage30(struct mk_plugin *p,
+ struct mk_http_session *cs,
+ struct mk_http_request *sr,
+ int n_params,
+ struct mk_list *params)
+{
+ (void) p;
+ (void) cs;
+ (void) n_params;
+ (void) params;
+
+ struct mk_http_header *header;
+
+ PLUGIN_TRACE("[FD %i] Mandril validating URL", cs->socket);
+
+ if (mk_security_check_url(p, sr->uri_processed) < 0) {
+ PLUGIN_TRACE("[FD %i] Close connection, blocked URL", cs->socket);
+ p->api->header_set_http_status(sr, MK_CLIENT_FORBIDDEN);
+ return MK_PLUGIN_RET_CLOSE_CONX;
+ }
+
+ PLUGIN_TRACE("[FD %d] Mandril validating hotlinking", cs->socket);
+
+ header = p->api->header_get(MK_HEADER_REFERER, sr, NULL, 0);
+ if (mk_security_check_hotlink(p, sr->uri_processed, sr->host, header) < 0) {
+ PLUGIN_TRACE("[FD %i] Close connection, deny hotlinking.", cs->socket);
+ p->api->header_set_http_status(sr, MK_CLIENT_FORBIDDEN);
+ return MK_PLUGIN_RET_CLOSE_CONX;
+ }
+
+ return MK_PLUGIN_RET_NOT_ME;
+}
+
+struct mk_plugin_stage mk_plugin_stage_mandril = {
+ .stage10 = &mk_mandril_stage10,
+ .stage30 = &mk_mandril_stage30
+};
+
+struct mk_plugin mk_plugin_mandril = {
+ /* Identification */
+ .shortname = "mandril",
+ .name = "Mandril Security",
+ .version = MK_VERSION_STR,
+ .hooks = MK_PLUGIN_STAGE,
+
+ /* Init / Exit */
+ .init_plugin = mk_mandril_plugin_init,
+ .exit_plugin = mk_mandril_plugin_exit,
+
+ /* Init Levels */
+ .master_init = NULL,
+ .worker_init = NULL,
+
+ /* Type */
+ .stage = &mk_plugin_stage_mandril
+};
diff --git a/fluent-bit/lib/monkey/plugins/mandril/mandril.h b/fluent-bit/lib/monkey/plugins/mandril/mandril.h
new file mode 100644
index 000000000..6cadf3857
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/mandril/mandril.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ * Copyright 2012, Sonny Karlsson
+ *
+ * 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.
+ */
+
+/* security.c */
+#ifndef MK_SECURITY_H
+#define MK_SECURITY_H
+
+struct mk_secure_ip_t
+{
+ struct in_addr ip;
+
+ /* if subnet is true, next fields are populated */
+ int is_subnet;
+
+ int network;
+ int netmask;
+ unsigned int hostmin;
+ unsigned int hostmax;
+
+ /* list head linker */
+ struct mk_list _head;
+};
+
+struct mk_secure_url_t
+{
+ char *criteria;
+ struct mk_list _head;
+};
+
+struct mk_secure_deny_hotlink_t
+{
+ char *criteria;
+ struct mk_list _head;
+};
+
+struct mk_list mk_secure_ip;
+struct mk_list mk_secure_url;
+struct mk_list mk_secure_deny_hotlink;
+
+#endif
diff --git a/fluent-bit/lib/monkey/plugins/tls/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/tls/CMakeLists.txt
new file mode 100644
index 000000000..2bde86cac
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/tls/CMakeLists.txt
@@ -0,0 +1,17 @@
+set(src
+ tls.c
+)
+
+if(NOT WITH_MBEDTLS_SHARED)
+ option(ENABLE_TESTING OFF)
+ option(ENABLE_PROGRAMS OFF)
+ option(INSTALL_MBEDTLS_HEADERS OFF)
+ set(MK_MBEDTLS_SRC ../../deps/mbedtls-2.4.2)
+ add_subdirectory(${MK_MBEDTLS_SRC} ${CMAKE_BINARY_DIR}/mbedtls-2.4.2)
+ include_directories(${MK_MBEDTLS_SRC}/include)
+endif()
+
+MONKEY_PLUGIN(tls "${src}")
+
+MONKEY_PLUGIN_LINK_LIB(tls mbedtls)
+add_subdirectory(conf)
diff --git a/fluent-bit/lib/monkey/plugins/tls/conf/CMakeLists.txt b/fluent-bit/lib/monkey/plugins/tls/conf/CMakeLists.txt
new file mode 100644
index 000000000..c1886d340
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/tls/conf/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(conf_dir "${MK_PATH_CONF}/plugins/tls/")
+
+install(DIRECTORY DESTINATION ${conf_dir})
+
+if(BUILD_LOCAL)
+ file(COPY tls.conf DESTINATION ${conf_dir})
+else()
+ install(FILES tls.conf DESTINATION ${conf_dir})
+endif()
diff --git a/fluent-bit/lib/monkey/plugins/tls/conf/tls.conf b/fluent-bit/lib/monkey/plugins/tls/conf/tls.conf
new file mode 100644
index 000000000..fcf00517a
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/tls/conf/tls.conf
@@ -0,0 +1,25 @@
+[TLS]
+ # If no absolute path is given, files are assumed to be located
+ # in plugin configuration directory. All files are assumed to be
+ # stored in PEM format.
+
+ # Server certificate
+ #
+ CertificateFile srv_cert.pem
+
+ # Certificate chain
+ #
+ # Not required, but can speed-up handshakes.
+ #
+ # CertificateChainFile srv_cert_chain.pem
+
+ # Server RSA key
+ #
+ RSAKeyFile rsa_key.pem
+
+ # Diffie-Hellman parameters
+ #
+ # Generate using openssl:
+ # $ openssl dhparam -out dhparam.pem 1024
+ #
+ DHParameterFile dhparam.pem
diff --git a/fluent-bit/lib/monkey/plugins/tls/tls.c b/fluent-bit/lib/monkey/plugins/tls/tls.c
new file mode 100644
index 000000000..94c2afc58
--- /dev/null
+++ b/fluent-bit/lib/monkey/plugins/tls/tls.c
@@ -0,0 +1,862 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <pthread.h>
+
+#include <mbedtls/version.h>
+#include <mbedtls/error.h>
+#include <mbedtls/net.h>
+#include <mbedtls/ssl.h>
+#include <mbedtls/bignum.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/certs.h>
+#include <mbedtls/x509.h>
+#include <mbedtls/ssl_cache.h>
+#include <mbedtls/pk.h>
+#include <mbedtls/dhm.h>
+#include <monkey/mk_api.h>
+
+#ifndef SENDFILE_BUF_SIZE
+#define SENDFILE_BUF_SIZE MBEDTLS_SSL_MAX_CONTENT_LEN
+#endif
+
+#ifndef POLAR_DEBUG_LEVEL
+#define POLAR_DEBUG_LEVEL 0
+#endif
+
+#if (!defined(MBEDTLS_BIGNUM_C) || !defined(MBEDTLS_ENTROPY_C) || \
+ !defined(MBEDTLS_SSL_TLS_C) || !defined(MBEDTLS_SSL_SRV_C) || \
+ !defined(MBEDTLS_NET_C) || !defined(MBEDTLS_RSA_C) || \
+ !defined(MBEDTLS_CTR_DRBG_C))
+#error "One or more required POLARSSL modules not built."
+#endif
+
+struct polar_config {
+ char *cert_file;
+ char *cert_chain_file;
+ char *key_file;
+ char *dh_param_file;
+ int8_t check_client_cert;
+};
+
+#if defined(MBEDTLS_SSL_CACHE_C)
+struct polar_sessions {
+ pthread_mutex_t _mutex;
+ mbedtls_ssl_cache_context cache;
+};
+
+static struct polar_sessions global_sessions = {
+ ._mutex = PTHREAD_MUTEX_INITIALIZER,
+};
+
+#endif
+
+struct polar_context_head {
+ mbedtls_ssl_context context;
+ int fd;
+ struct polar_context_head *_next;
+};
+
+struct polar_thread_context {
+
+ struct polar_context_head *contexts;
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_pk_context pkey;
+ mbedtls_ssl_config conf;
+
+ struct mk_list _head;
+};
+
+struct polar_server_context {
+
+ struct polar_config config;
+ mbedtls_x509_crt cert;
+ mbedtls_x509_crt ca_cert;
+ pthread_mutex_t mutex;
+ mbedtls_dhm_context dhm;
+ mbedtls_entropy_context entropy;
+ struct polar_thread_context threads;
+};
+
+struct polar_server_context *server_context;
+static const char *my_dhm_P = MBEDTLS_DHM_RFC5114_MODP_2048_P;
+static const char *my_dhm_G = MBEDTLS_DHM_RFC5114_MODP_2048_G;
+
+static pthread_key_t local_context;
+
+/*
+ * The following function is taken from PolarSSL sources to get
+ * the number of available bytes to read from a buffer.
+ *
+ * We copy this to make it inline and avoid extra context switches
+ * on each read routine.
+ */
+static inline size_t polar_get_bytes_avail(const mbedtls_ssl_context *ssl)
+{
+ return (ssl->in_offt == NULL ? 0 : ssl->in_msglen);
+}
+
+static struct polar_thread_context *local_thread_context(void)
+{
+ return pthread_getspecific(local_context);
+}
+
+#if (POLAR_DEBUG_LEVEL > 0)
+static void polar_debug(void *ctx, int level, const char *str)
+{
+ (void)ctx;
+
+ if (level < POLAR_DEBUG_LEVEL) {
+ mk_warn("%.*s", (int)strlen(str) - 1, str);
+ }
+}
+#endif
+
+static int handle_return(int ret)
+{
+#if defined(TRACE)
+ char err_buf[72];
+ if (ret < 0) {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ PLUGIN_TRACE("[tls] SSL error: %s", err_buf);
+ }
+#endif
+ if (ret < 0) {
+ switch( ret )
+ {
+ case MBEDTLS_ERR_SSL_WANT_READ:
+ case MBEDTLS_ERR_SSL_WANT_WRITE:
+ if (errno != EAGAIN)
+ errno = EAGAIN;
+ return -1;
+ case MBEDTLS_ERR_SSL_CONN_EOF:
+ return 0;
+ default:
+ if (errno == EAGAIN)
+ errno = 0;
+ return -1;
+ }
+ }
+ else {
+ return ret;
+ }
+}
+
+static int tls_cache_get(void *p, mbedtls_ssl_session *session)
+{
+ struct polar_sessions *session_cache;
+ int ret;
+
+ session_cache = p;
+ pthread_mutex_lock(&session_cache->_mutex);
+ ret = mbedtls_ssl_cache_get(&session_cache->cache, session);
+ pthread_mutex_unlock(&session_cache->_mutex);
+
+ return ret;
+}
+
+static int tls_cache_set(void *p, const mbedtls_ssl_session *session)
+{
+ struct polar_sessions *session_cache;
+ int ret;
+
+ session_cache = p;
+ pthread_mutex_lock(&session_cache->_mutex);
+ ret = mbedtls_ssl_cache_set(&session_cache->cache, session);
+ pthread_mutex_unlock(&session_cache->_mutex);
+
+ return ret;
+}
+
+static int config_parse(const char *confdir, struct polar_config *conf)
+{
+ long unsigned int len;
+ char *conf_path = NULL;
+ char *cert_file = NULL;
+ char *cert_chain_file = NULL;
+ char *key_file = NULL;
+ char *dh_param_file = NULL;
+ int8_t check_client_cert = MK_FALSE;
+ struct mk_rconf_section *section;
+ struct mk_rconf *conf_head;
+
+ mk_api->str_build(&conf_path, &len, "%stls.conf", confdir);
+ conf_head = mk_api->config_open(conf_path);
+ mk_api->mem_free(conf_path);
+
+ if (conf_head == NULL) {
+ goto fallback;
+ }
+
+ section = mk_api->config_section_get(conf_head, "TLS");
+ if (!section) {
+ goto fallback;
+ }
+
+ cert_file = mk_api->config_section_get_key(section,
+ "CertificateFile",
+ MK_RCONF_STR);
+ cert_chain_file = mk_api->config_section_get_key(section,
+ "CertificateChainFile",
+ MK_RCONF_STR);
+ key_file = mk_api->config_section_get_key(section,
+ "RSAKeyFile",
+ MK_RCONF_STR);
+ dh_param_file = mk_api->config_section_get_key(section,
+ "DHParameterFile",
+ MK_RCONF_STR);
+
+ check_client_cert = mk_api->config_section_get_key(section,
+ "CheckClientCert",
+ MK_RCONF_BOOL);
+fallback:
+ /* Set default name if not specified */
+ if (!cert_file) {
+ mk_api->str_build(&conf->cert_file, &len,
+ "%ssrv_cert.pem", confdir);
+ }
+ else {
+ /* Set absolute path or compose a new one based on the relative */
+ if (*cert_file == '/') {
+ conf->cert_file = cert_file;
+ }
+ else {
+ mk_api->str_build(&conf->cert_file, &len,
+ "%s/%s", confdir, cert_file);
+ }
+ }
+
+ /* Set default name if not specified */
+ if (cert_chain_file) {
+ /* Set absolute path or compose a new one based on the relative */
+ if (*cert_chain_file == '/') {
+ conf->cert_chain_file = cert_chain_file;
+ }
+ else {
+ mk_api->str_build(&conf->cert_chain_file, &len,
+ "%s/%s", confdir, cert_chain_file);
+ }
+ }
+ else {
+ conf->cert_chain_file = NULL;
+ }
+
+ /* Set default name if not specified */
+ if (!key_file) {
+ mk_api->str_build(&conf->key_file, &len,
+ "%srsa.pem", confdir);
+ }
+ else {
+ /* Set absolute path or compose a new one based on the relative */
+ if (*key_file == '/') {
+ conf->key_file = key_file;
+ }
+ else {
+ mk_api->str_build(&conf->key_file, &len,
+ "%s/%s", confdir, key_file);
+ }
+ }
+
+ /* Set default name if not specified */
+ if (!dh_param_file) {
+ mk_api->str_build(&conf->dh_param_file, &len,
+ "%sdhparam.pem", confdir);
+ }
+ else {
+ /* Set absolute path or compose a new one based on the relative */
+ if (*dh_param_file == '/') {
+ conf->dh_param_file = dh_param_file;
+ }
+ else {
+ mk_api->str_build(&conf->dh_param_file, &len,
+ "%s/%s", confdir, dh_param_file);
+ }
+ }
+
+ /* Set client cert check */
+ conf->check_client_cert = check_client_cert;
+
+ if (conf_head) {
+ mk_api->config_free(conf_head);
+ }
+
+ return 0;
+}
+
+static int polar_load_certs(const struct polar_config *conf)
+{
+ char err_buf[72];
+ int ret = -1;
+
+ ret = mbedtls_x509_crt_parse_file(&server_context->cert, conf->cert_file);
+ if (ret < 0) {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ mk_warn("[tls] Load cert '%s' failed: %s",
+ conf->cert_file,
+ err_buf);
+
+#if defined(MBEDTLS_CERTS_C)
+ mk_warn("[tls] Using test certificates, "
+ "please set 'CertificateFile' in tls.conf");
+
+ ret = mbedtls_x509_crt_parse(&server_context->cert,
+ (unsigned char *)mbedtls_test_srv_crt, strlen(mbedtls_test_srv_crt));
+
+ if (ret) {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ mk_warn("[tls] Load built-in cert failed: %s", err_buf);
+ return -1;
+ }
+
+ return 0;
+#else
+ return -1;
+#endif // defined(MBEDTLS_CERTS_C)
+ }
+ else if (conf->cert_chain_file != NULL) {
+ ret = mbedtls_x509_crt_parse_file(&server_context->ca_cert,
+ conf->cert_chain_file);
+
+ if (ret) {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ mk_warn("[tls] Load cert chain '%s' failed: %s",
+ conf->cert_chain_file,
+ err_buf);
+ }
+ }
+
+ return 0;
+}
+
+static int polar_load_key(struct polar_thread_context *thread_context,
+ const struct polar_config *conf)
+{
+ char err_buf[72];
+ int ret;
+
+ assert(conf->key_file);
+
+ ret = mbedtls_pk_parse_keyfile(&thread_context->pkey, conf->key_file, NULL);
+ if (ret < 0) {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ MK_TRACE("[tls] Load key '%s' failed: %s",
+ conf->key_file,
+ err_buf);
+
+#if defined(MBEDTLS_CERTS_C)
+
+ ret = mbedtls_pk_parse_key(&thread_context->pkey,
+ (unsigned char *)mbedtls_test_srv_key,
+ strlen(mbedtls_test_srv_key), NULL, 0);
+ if (ret) {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ mk_err("[tls] Failed to load built-in RSA key: %s", err_buf);
+ return -1;
+ }
+#else
+ return -1;
+#endif // defined(MBEDTLS_CERTS_C)
+ }
+ return 0;
+}
+
+static int polar_load_dh_param(const struct polar_config *conf)
+{
+ char err_buf[72];
+ int ret;
+
+ assert(conf->dh_param_file);
+
+ ret = mbedtls_dhm_parse_dhmfile(&server_context->dhm, conf->dh_param_file);
+ if (ret < 0) {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+
+ ret = mbedtls_mpi_read_string(&server_context->dhm.P, 16, my_dhm_P);
+ if (ret < 0) {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ mk_err("[tls] Load DH parameter failed: %s", err_buf);
+ return -1;
+ }
+ ret = mbedtls_mpi_read_string(&server_context->dhm.G, 16, my_dhm_G);
+ if (ret < 0) {
+ mbedtls_strerror(ret, err_buf, sizeof(err_buf));
+ mk_err("[tls] Load DH parameter failed: %s", err_buf);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int mk_tls_init()
+{
+ pthread_key_create(&local_context, NULL);
+
+#if defined(MBEDTLS_SSL_CACHE_C)
+ mbedtls_ssl_cache_init(&global_sessions.cache);
+#endif
+
+ pthread_mutex_lock(&server_context->mutex);
+ mk_list_init(&server_context->threads._head);
+ mbedtls_entropy_init(&server_context->entropy);
+ pthread_mutex_unlock(&server_context->mutex);
+
+ PLUGIN_TRACE("[tls] Load certificates.");
+ if (polar_load_certs(&server_context->config)) {
+ return -1;
+ }
+ PLUGIN_TRACE("[tls] Load DH parameters.");
+ if (polar_load_dh_param(&server_context->config)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int entropy_func_safe(void *data, unsigned char *output, size_t len)
+{
+ int ret;
+
+ pthread_mutex_lock(&server_context->mutex);
+ ret = mbedtls_entropy_func(data, output, len);
+ pthread_mutex_unlock(&server_context->mutex);
+
+ return ret;
+}
+
+static void contexts_free(struct polar_context_head *ctx)
+{
+ struct polar_context_head *cur, *next;
+
+ if (ctx != NULL) {
+ cur = ctx;
+ next = cur->_next;
+
+ for (; next; cur = next, next = next->_next) {
+ mbedtls_ssl_free(&cur->context);
+ memset(cur, 0, sizeof(*cur));
+ mk_api->mem_free(cur);
+ }
+
+ mbedtls_ssl_free(&cur->context);
+ memset(cur, 0, sizeof(*cur));
+ mk_api->mem_free(cur);
+ }
+}
+
+static void config_free(struct polar_config *conf)
+{
+ if (conf->cert_file) mk_api->mem_free(conf->cert_file);
+ if (conf->cert_chain_file) mk_api->mem_free(conf->cert_chain_file);
+ if (conf->key_file) mk_api->mem_free(conf->key_file);
+ if (conf->dh_param_file) mk_api->mem_free(conf->dh_param_file);
+}
+
+/* Contexts may be requested from outside workers on exit so we should
+ * be prepared for an empty context.
+ */
+static mbedtls_ssl_context *context_get(int fd)
+{
+ struct polar_thread_context *thctx = local_thread_context();
+ struct polar_context_head **cur = &thctx->contexts;
+
+ if (cur == NULL) {
+ return NULL;
+ }
+
+ for (; *cur; cur = &(*cur)->_next) {
+ if ((*cur)->fd == fd) {
+ return &(*cur)->context;
+ }
+ }
+
+ return NULL;
+}
+
+static mbedtls_ssl_context *context_new(int fd)
+{
+ struct polar_thread_context *thctx = local_thread_context();
+ struct polar_context_head **cur = &thctx->contexts;
+ mbedtls_ssl_context *ssl = NULL;
+ mbedtls_ssl_cache_context cache;
+
+ mbedtls_ssl_cache_init(&cache);
+
+ assert(cur != NULL);
+
+ for (; *cur; cur = &(*cur)->_next) {
+ if ((*cur)->fd == -1) {
+ break;
+ }
+ }
+
+ if (*cur == NULL) {
+ PLUGIN_TRACE("[polarssl %d] New ssl context.", fd);
+
+ *cur = mk_api->mem_alloc(sizeof(**cur));
+ if (*cur == NULL) {
+ return NULL;
+ }
+ (*cur)->_next = NULL;
+
+ ssl = &(*cur)->context;
+
+ mbedtls_ssl_init(ssl);
+ mbedtls_ssl_setup(ssl, &thctx->conf);
+
+ mbedtls_ssl_conf_session_cache(&thctx->conf,
+ &global_sessions,
+ tls_cache_get,
+ tls_cache_set);
+
+ mbedtls_ssl_set_bio(ssl, &(*cur)->fd,
+ mbedtls_net_send, mbedtls_net_recv, NULL);
+
+ mbedtls_ssl_conf_rng(&thctx->conf, mbedtls_ctr_drbg_random,
+ &thctx->ctr_drbg);
+
+#if (POLAR_DEBUG_LEVEL > 0)
+ mbedtls_ssl_conf_dbg(ssl, polar_debug, 0);
+#endif
+
+ mbedtls_ssl_conf_own_cert(&thctx->conf, &server_context->cert, &thctx->pkey);
+ mbedtls_ssl_conf_ca_chain(&thctx->conf, &server_context->ca_cert, NULL);
+ mbedtls_ssl_conf_dh_param_ctx(&thctx->conf, &server_context->dhm);
+
+ if (server_context->config.check_client_cert == MK_TRUE) {
+ mbedtls_ssl_conf_authmode(&thctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
+ }
+ }
+ else {
+ ssl = &(*cur)->context;
+ }
+
+ (*cur)->fd = fd;
+
+ return ssl;
+}
+
+static int context_unset(int fd, mbedtls_ssl_context *ssl)
+{
+ struct polar_context_head *head;
+
+ head = container_of(ssl, struct polar_context_head, context);
+
+ if (head->fd == fd) {
+ head->fd = -1;
+ mbedtls_ssl_session_reset(ssl);
+ }
+ else {
+ mk_err("[polarssl %d] Context already unset.", fd);
+ }
+
+ return 0;
+}
+
+int mk_tls_read(int fd, void *buf, int count)
+{
+ size_t avail;
+ mbedtls_ssl_context *ssl = context_get(fd);
+
+ if (!ssl) {
+ ssl = context_new(fd);
+ }
+
+ int ret = handle_return(mbedtls_ssl_read(ssl, buf, count));
+ PLUGIN_TRACE("IN: %i SSL READ: %i ; CORE COUNT: %i",
+ ssl->in_msglen,
+ ret, count);
+
+ /* Check if the caller read less than the available data */
+ if (ret > 0) {
+ avail = polar_get_bytes_avail(ssl);
+ if (avail > 0) {
+ /*
+ * A read callback would never read in buffer more than
+ * the size specified in 'count', but it aims to return
+ * as value the total information read in the buffer plugin
+ */
+ ret += avail;
+ }
+ }
+ return ret;
+}
+
+int mk_tls_write(int fd, const void *buf, size_t count)
+{
+ mbedtls_ssl_context *ssl = context_get(fd);
+ if (!ssl) {
+ ssl = context_new(fd);
+ }
+
+ return handle_return(mbedtls_ssl_write(ssl, buf, count));
+}
+
+int mk_tls_writev(int fd, struct mk_iov *mk_io)
+{
+ mbedtls_ssl_context *ssl = context_get(fd);
+ const int iov_len = mk_io->iov_idx;
+ const struct iovec *io = mk_io->io;
+ const size_t len = mk_io->total_len;
+ unsigned char *buf;
+ size_t used = 0;
+ int ret = 0, i;
+
+ if (!ssl) {
+ ssl = context_new(fd);
+ }
+
+ buf = mk_api->mem_alloc(len);
+ if (buf == NULL) {
+ mk_err("malloc failed: %s", strerror(errno));
+ return -1;
+ }
+
+ for (i = 0; i < iov_len; i++) {
+ memcpy(buf + used, io[i].iov_base, io[i].iov_len);
+ used += io[i].iov_len;
+ }
+
+ assert(used == len);
+ ret = mbedtls_ssl_write(ssl, buf, len);
+ mk_api->mem_free(buf);
+
+ return handle_return(ret);
+}
+
+int mk_tls_send_file(int fd, int file_fd, off_t *file_offset,
+ size_t file_count)
+{
+ mbedtls_ssl_context *ssl = context_get(fd);
+ unsigned char *buf;
+ ssize_t used, remain = file_count, sent = 0;
+ int ret;
+
+ if (!ssl) {
+ ssl = context_new(fd);
+ }
+
+ buf = mk_api->mem_alloc(SENDFILE_BUF_SIZE);
+ if (buf == NULL) {
+ return -1;
+ }
+
+ do {
+ used = pread(file_fd, buf, SENDFILE_BUF_SIZE, *file_offset);
+ if (used == 0) {
+ ret = 0;
+ }
+ else if (used < 0) {
+ mk_err("[tls] Read from file failed: %s", strerror(errno));
+ ret = -1;
+ }
+ else if (remain > 0) {
+ ret = mbedtls_ssl_write(ssl, buf, used < remain ? used : remain);
+ }
+ else {
+ ret = mbedtls_ssl_write(ssl, buf, used);
+ }
+
+ if (ret > 0) {
+ if (remain > 0) {
+ remain -= ret;
+ }
+ sent += ret;
+ *file_offset += ret;
+ }
+ } while (ret > 0);
+
+ mk_api->mem_free(buf);
+
+ if (sent > 0) {
+ return sent;
+ }
+ else {
+ return handle_return(ret);
+ }
+}
+
+int mk_tls_close(int fd)
+{
+ mbedtls_ssl_context *ssl = context_get(fd);
+
+ PLUGIN_TRACE("[fd %d] Closing connection", fd);
+
+ if (ssl) {
+ mbedtls_ssl_close_notify(ssl);
+ context_unset(fd, ssl);
+ }
+
+ close(fd);
+ return 0;
+}
+
+int mk_tls_plugin_init(struct plugin_api **api, char *confdir)
+{
+ int used;
+ struct mk_list *head;
+ struct mk_config_listener *listen;
+
+ /* Evil global config stuff */
+ mk_api = *api;
+
+ /* Check if the plugin will be used by some listener */
+ used = MK_FALSE;
+ mk_list_foreach(head, &mk_api->config->listeners) {
+ listen = mk_list_entry(head, struct mk_config_listener, _head);
+ if (listen->flags & MK_CAP_SOCK_TLS) {
+ used = MK_TRUE;
+ break;
+ }
+ }
+
+ if (used) {
+ /* If it's used, load certificates.. mandatory */
+ server_context = mk_api->mem_alloc_z(sizeof(struct polar_server_context));
+ config_parse(confdir, &server_context->config);
+ return mk_tls_init();
+ }
+ else {
+ /* Plugin is not used, just unregister in silence */
+ return -2;
+ }
+}
+
+void mk_tls_worker_init(void)
+{
+ int ret;
+ struct polar_thread_context *thctx;
+ const char *pers = "monkey";
+
+ PLUGIN_TRACE("[tls] Init thread context.");
+
+ thctx = mk_api->mem_alloc(sizeof(*thctx));
+ if (thctx == NULL) {
+ goto error;
+ }
+ thctx->contexts = NULL;
+ mk_list_init(&thctx->_head);
+
+
+ /* SSL confniguration */
+ mbedtls_ssl_config_init(&thctx->conf);
+ mbedtls_ssl_config_defaults(&thctx->conf,
+ MBEDTLS_SSL_IS_SERVER,
+ MBEDTLS_SSL_TRANSPORT_STREAM,
+ MBEDTLS_SSL_PRESET_DEFAULT);
+
+ pthread_mutex_lock(&server_context->mutex);
+ mk_list_add(&thctx->_head, &server_context->threads._head);
+ pthread_mutex_unlock(&server_context->mutex);
+
+ mbedtls_ctr_drbg_init(&thctx->ctr_drbg);
+ ret = mbedtls_ctr_drbg_seed(&thctx->ctr_drbg,
+ entropy_func_safe, &server_context->entropy,
+ (const unsigned char *) pers,
+ strlen(pers));
+ if (ret != 0) {
+ goto error;
+ }
+
+ mbedtls_pk_init(&thctx->pkey);
+
+ PLUGIN_TRACE("[tls] Load RSA key.");
+ if (polar_load_key(thctx, &server_context->config)) {
+ goto error;
+ }
+
+ PLUGIN_TRACE("[tls] Set local thread context.");
+ pthread_setspecific(local_context, thctx);
+
+ return;
+
+ error:
+ exit(EXIT_FAILURE);
+}
+
+int mk_tls_plugin_exit()
+{
+ struct mk_list *cur, *tmp;
+ struct polar_thread_context *thctx;
+
+ mbedtls_x509_crt_free(&server_context->cert);
+ mbedtls_x509_crt_free(&server_context->ca_cert);
+ mbedtls_dhm_free(&server_context->dhm);
+
+ mk_list_foreach_safe(cur, tmp, &server_context->threads._head) {
+ thctx = mk_list_entry(cur, struct polar_thread_context, _head);
+ contexts_free(thctx->contexts);
+ mk_api->mem_free(thctx);
+
+ mbedtls_pk_free(&thctx->pkey);
+ }
+ pthread_mutex_destroy(&server_context->mutex);
+
+#if defined(MBEDTLS_SSL_CACHE_C)
+ mbedtls_ssl_cache_free(&global_sessions.cache);
+#endif
+
+ config_free(&server_context->config);
+ mk_api->mem_free(server_context);
+
+ return 0;
+}
+
+/* Network Layer plugin Callbacks */
+struct mk_plugin_network mk_plugin_network_tls = {
+ .read = mk_tls_read,
+ .write = mk_tls_write,
+ .writev = mk_tls_writev,
+ .close = mk_tls_close,
+ .send_file = mk_tls_send_file,
+ .buffer_size = MBEDTLS_SSL_MAX_CONTENT_LEN
+};
+
+struct mk_plugin mk_plugin_tls = {
+ /* Identification */
+ .shortname = "tls",
+ .name = "SSL/TLS Network Layer",
+ .version = MK_VERSION_STR,
+ .hooks = MK_PLUGIN_NETWORK_LAYER,
+
+ /* Init / Exit */
+ .init_plugin = mk_tls_plugin_init,
+ .exit_plugin = mk_tls_plugin_exit,
+
+ /* Init Levels */
+ .master_init = NULL,
+ .worker_init = mk_tls_worker_init,
+
+ /* Type */
+ .network = &mk_plugin_network_tls,
+ .capabilities = MK_CAP_SOCK_TLS
+};