summaryrefslogtreecommitdiffstats
path: root/libnetdata/socket
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2022-10-05 14:13:46 +0300
committerGitHub <noreply@github.com>2022-10-05 14:13:46 +0300
commit8fc3b351a2e7fc96eced8f924de2e9cec9842128 (patch)
treebde41c66573ccaf8876c280e00742cc6096b587c /libnetdata/socket
parent6850878e697d66dc90b9af1e750b22238c63c292 (diff)
Allow netdata plugins to expose functions for querying more information about specific charts (#13720)
* function renames and code cleanup in popen.c; no actual code changes * netdata popen() now opens both child process stdin and stdout and returns FILE * for both * pass both input and output to parser structures * updated rrdset to call custom functions * RRDSET FUNCTION leading calls for both sync and async operation * put RRDSET functions to a separate file * added format and timeout at function definition * support for synchronous (internal plugins) and asynchronous (external plugins and children) functions * /api/v1/function endpoint * functions are now attached to the host and there is a dictionary view per chart * functions implemented at plugins.d * remove the defer until keyword hook from plugins.d when it is done * stream sender implementation of functions * sanitization of all functions so that certain characters are only allowed * strictier sanitization * common max size * 1st working plugins.d example * always init inflight dictionary * properly destroy dictionaries to avoid parallel insertion of items * add more debugging on disconnection reasons * add more debugging on disconnection reasons again * streaming receiver respects newlines * dont use the same fp for both streaming receive and send * dont free dbengine memory with internal checks * make sender proceed in the buffer * added timing info and garbage collection at plugins.d * added info about routing nodes * added info about routing nodes with delay * added more info about delays * added more info about delays again * signal sending thread to wake up * streaming version labeling and commented code to support capabilities * added functions to /api/v1/data, /api/v1/charts, /api/v1/chart, /api/v1/info * redirect top output to stdout * address coverity findings * fix resource leaks of popen * log attempts to connect to individual destinations * better messages * properly parse destinations * try to find a function from the most matching to the least matching * log added streaming destinations * rotate destinations bypassing a node in the middle that does not accept our connection * break the loops properly * use typedef to define callbacks * capabilities negotiation during streaming * functions exposed upstream based on capabilities; compression disabled per node persisting reconnects; always try to connect with all capabilities * restore functionality to lookup functions * better logging of capabilities * remove old versions from capabilities when a newer version is there * fix formatting * optimization for plugins.d rrdlabels to avoid creating and destructing dictionaries all the time * delayed health initialization for rrddim and rrdset * cleanup health initialization * fix for popen() not returning the right value * add health worker jobs for initializing rrdset and rrddim * added content type support for functions; apps.plugin permanent function to display all the processes * fixes for functions parameters parsing in apps.plugin * fix for process matching in apps.plugiin * first working function for apps.plugin * Dashboard ACL is disabled for functions; Function errors are all in JSON format * apps.plugin function processes returns json table * use json_escape_string() to escape message * fix formatting * apps.plugin exposes all its metrics to function processes * fix json formatting when filtering out some rows * reopen the internal pipe of rrdpush in case of errors * misplaced statement * do not use buffer->len * support for GLOBAL functions (functions that are not linked to a chart * added /api/v1/functions endpoint; removed format from the FUNCTIONS api; * swagger documentation about the new api end points * added plugins.d documentation about functions * never re-close a file * remove uncessesary ifdef * fixed issues identified by codacy * fix for null label value * make edit-config copy-and-paste friendly * Revert "make edit-config copy-and-paste friendly" This reverts commit 54500c0e0a97f65a0c66c4d34e966f6a9056698e. * reworked sender handshake to fix coverity findings * timeout is zero, for both send_timeout() and recv_timeout() * properly detect that parent closed the socket * support caching of function responses; limit function response to 10MB; added protection from malformed function responses * disabled excessive logging * added units to apps.plugin function processes and normalized all values to be human readable * shorter field names * fixed issues reported * fixed apps.plugin error response; tested that pluginsd can properly handle faulty responses * use double linked list macros for double linked list management * faster apps.plugin function printing by minimizing file operations * added memory percentage * fix compatibility issues with older compilers and FreeBSD * rrdpush sender code cleanup; rrhost structure cleanup from sender flags and variables; * fix letftover variable in ifdef * apps.plugin: do not call detach from the thread; exit immediately when input is broken * exclude AR charts from health * flush cleaner; prefer sender output * clarity * do not fill the cbuffer if not connected * fix * dont enabled host->sender if streaming is not enabled; send host label updates to parent; * functions are only available through ACLK * Prepared statement reports only in dev mode * fix AR chart detection * fix for streaming not being enabling itself * more cleanup of sender and receiver structures * moved read-only flags and configuration options to rrdhost->options * fixed merge with master * fix for incomplete rename * prevent service thread from working on charts that are being collected Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>
Diffstat (limited to 'libnetdata/socket')
-rw-r--r--libnetdata/socket/security.c45
-rw-r--r--libnetdata/socket/security.h14
-rw-r--r--libnetdata/socket/socket.c101
-rw-r--r--libnetdata/socket/socket.h28
4 files changed, 124 insertions, 64 deletions
diff --git a/libnetdata/socket/security.c b/libnetdata/socket/security.c
index 6ac512de5e..f229b7aa36 100644
--- a/libnetdata/socket/security.c
+++ b/libnetdata/socket/security.c
@@ -2,14 +2,14 @@
#ifdef ENABLE_HTTPS
-SSL_CTX *netdata_exporting_ctx=NULL;
-SSL_CTX *netdata_client_ctx=NULL;
-SSL_CTX *netdata_srv_ctx=NULL;
-const char *security_key=NULL;
-const char *security_cert=NULL;
+SSL_CTX *netdata_ssl_exporting_ctx =NULL;
+SSL_CTX *netdata_ssl_client_ctx =NULL;
+SSL_CTX *netdata_ssl_srv_ctx =NULL;
+const char *ssl_security_key =NULL;
+const char *ssl_security_cert =NULL;
const char *tls_version=NULL;
const char *tls_ciphers=NULL;
-int netdata_validate_server = NETDATA_SSL_VALID_CERTIFICATE;
+int netdata_ssl_validate_server = NETDATA_SSL_VALID_CERTIFICATE;
/**
* Info Callback
@@ -161,7 +161,7 @@ static SSL_CTX * security_initialize_openssl_server() {
return NULL;
}
- SSL_CTX_use_certificate_file(ctx, security_cert, SSL_FILETYPE_PEM);
+ SSL_CTX_use_certificate_file(ctx, ssl_security_cert, SSL_FILETYPE_PEM);
#else
ctx = SSL_CTX_new(TLS_server_method());
if (!ctx) {
@@ -169,11 +169,11 @@ static SSL_CTX * security_initialize_openssl_server() {
return NULL;
}
- SSL_CTX_use_certificate_chain_file(ctx, security_cert);
+ SSL_CTX_use_certificate_chain_file(ctx, ssl_security_cert);
#endif
security_openssl_common_options(ctx, 0);
- SSL_CTX_use_PrivateKey_file(ctx,security_key,SSL_FILETYPE_PEM);
+ SSL_CTX_use_PrivateKey_file(ctx, ssl_security_key,SSL_FILETYPE_PEM);
if (!SSL_CTX_check_private_key(ctx)) {
ERR_error_string_n(ERR_get_error(),lerror,sizeof(lerror));
@@ -207,24 +207,25 @@ void security_start_ssl(int selector) {
switch (selector) {
case NETDATA_SSL_CONTEXT_SERVER: {
struct stat statbuf;
- if (stat(security_key, &statbuf) || stat(security_cert, &statbuf)) {
+ if (stat(ssl_security_key, &statbuf) || stat(ssl_security_cert, &statbuf)) {
info("To use encryption it is necessary to set \"ssl certificate\" and \"ssl key\" in [web] !\n");
return;
}
- netdata_srv_ctx = security_initialize_openssl_server();
- SSL_CTX_set_mode(netdata_srv_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ netdata_ssl_srv_ctx = security_initialize_openssl_server();
+ SSL_CTX_set_mode(netdata_ssl_srv_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
break;
}
case NETDATA_SSL_CONTEXT_STREAMING: {
- netdata_client_ctx = security_initialize_openssl_client();
+ netdata_ssl_client_ctx = security_initialize_openssl_client();
//This is necessary for the stream, because it is working sometimes with nonblock socket.
//It returns the bitmask after to change, there is not any description of errors in the documentation
- SSL_CTX_set_mode(netdata_client_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |SSL_MODE_AUTO_RETRY);
+ SSL_CTX_set_mode(
+ netdata_ssl_client_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |SSL_MODE_AUTO_RETRY);
break;
}
case NETDATA_SSL_CONTEXT_EXPORTING: {
- netdata_exporting_ctx = security_initialize_openssl_client();
+ netdata_ssl_exporting_ctx = security_initialize_openssl_client();
break;
}
}
@@ -237,16 +238,16 @@ void security_start_ssl(int selector) {
*/
void security_clean_openssl()
{
- if (netdata_srv_ctx) {
- SSL_CTX_free(netdata_srv_ctx);
+ if (netdata_ssl_srv_ctx) {
+ SSL_CTX_free(netdata_ssl_srv_ctx);
}
- if (netdata_client_ctx) {
- SSL_CTX_free(netdata_client_ctx);
+ if (netdata_ssl_client_ctx) {
+ SSL_CTX_free(netdata_ssl_client_ctx);
}
- if (netdata_exporting_ctx) {
- SSL_CTX_free(netdata_exporting_ctx);
+ if (netdata_ssl_exporting_ctx) {
+ SSL_CTX_free(netdata_ssl_exporting_ctx);
}
#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110
@@ -355,7 +356,7 @@ int security_test_certificate(SSL *ssl) {
*
* @return It returns 0 on success and -1 otherwise.
*/
-int security_location_for_context(SSL_CTX *ctx, char *file, char *path) {
+int ssl_security_location_for_context(SSL_CTX *ctx, char *file, char *path) {
struct stat statbuf;
if (stat(file, &statbuf)) {
info("Netdata does not have the parent's SSL certificate, so it will use the default OpenSSL configuration to validate certificates!");
diff --git a/libnetdata/socket/security.h b/libnetdata/socket/security.h
index dbf71a6fea..a9b41fac46 100644
--- a/libnetdata/socket/security.h
+++ b/libnetdata/socket/security.h
@@ -42,15 +42,15 @@ struct netdata_ssl{
uint32_t flags; //The flags for SSL connection
};
-extern SSL_CTX *netdata_exporting_ctx;
-extern SSL_CTX *netdata_client_ctx;
-extern SSL_CTX *netdata_srv_ctx;
-extern const char *security_key;
-extern const char *security_cert;
+extern SSL_CTX *netdata_ssl_exporting_ctx;
+extern SSL_CTX *netdata_ssl_client_ctx;
+extern SSL_CTX *netdata_ssl_srv_ctx;
+extern const char *ssl_security_key;
+extern const char *ssl_security_cert;
extern const char *tls_version;
extern const char *tls_ciphers;
-extern int netdata_validate_server;
-extern int security_location_for_context(SSL_CTX *ctx,char *file,char *path);
+extern int netdata_ssl_validate_server;
+extern int ssl_security_location_for_context(SSL_CTX *ctx,char *file,char *path);
void security_openssl_library();
void security_clean_openssl();
diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c
index df6d3148ba..fd8613030e 100644
--- a/libnetdata/socket/socket.c
+++ b/libnetdata/socket/socket.c
@@ -779,6 +779,10 @@ int connect_to_this(const char *definition, int default_port, struct timeval *ti
char *path = host + 5;
return connect_to_unix(path, timeout);
}
+ else if(*host == '/') {
+ char *path = host;
+ return connect_to_unix(path, timeout);
+ }
char *e = host;
if(*e == '[') {
@@ -826,41 +830,92 @@ int connect_to_this(const char *definition, int default_port, struct timeval *ti
return connect_to_this_ip46(protocol, socktype, host, scope_id, service, timeout);
}
-int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) {
- int sock = -1;
-
+void foreach_entry_in_connection_string(const char *destination, bool (*callback)(char *entry, void *data), void *data) {
const char *s = destination;
while(*s) {
const char *e = s;
- // skip path, moving both s(tart) and e(nd)
- if(*e == '/')
- while(!isspace(*e) && *e != ',') s = ++e;
-
// skip separators, moving both s(tart) and e(nd)
while(isspace(*e) || *e == ',') s = ++e;
// move e(nd) to the first separator
- while(*e && !isspace(*e) && *e != ',' && *e != '/') e++;
+ while(*e && !isspace(*e) && *e != ',') e++;
// is there anything?
if(!*s || s == e) break;
char buf[e - s + 1];
strncpyz(buf, s, e - s);
- if(reconnects_counter) *reconnects_counter += 1;
- sock = connect_to_this(buf, default_port, timeout);
- if(sock != -1) {
- if(connected_to && connected_to_size) {
- strncpy(connected_to, buf, connected_to_size);
- connected_to[connected_to_size - 1] = '\0';
- }
- break;
- }
+
+ if(callback(buf, data)) break;
+
s = e;
}
+}
- return sock;
+struct connect_to_one_of_data {
+ int default_port;
+ struct timeval *timeout;
+ size_t *reconnects_counter;
+ char *connected_to;
+ size_t connected_to_size;
+ int sock;
+};
+
+static bool connect_to_one_of_callback(char *entry, void *data) {
+ struct connect_to_one_of_data *t = data;
+
+ if(t->reconnects_counter)
+ t->reconnects_counter++;
+
+ t->sock = connect_to_this(entry, t->default_port, t->timeout);
+ if(t->sock != -1) {
+ if(t->connected_to && t->connected_to_size) {
+ strncpyz(t->connected_to, entry, t->connected_to_size);
+ t->connected_to[t->connected_to_size - 1] = '\0';
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) {
+ struct connect_to_one_of_data t = {
+ .default_port = default_port,
+ .timeout = timeout,
+ .reconnects_counter = reconnects_counter,
+ .connected_to = connected_to,
+ .connected_to_size = connected_to_size,
+ .sock = -1,
+ };
+
+ foreach_entry_in_connection_string(destination, connect_to_one_of_callback, &t);
+
+ return t.sock;
+}
+
+static bool connect_to_one_of_urls_callback(char *entry, void *data) {
+ char *s = strchr(entry, '/');
+ if(s) *s = '\0';
+
+ return connect_to_one_of_callback(entry, data);
+}
+
+int connect_to_one_of_urls(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) {
+ struct connect_to_one_of_data t = {
+ .default_port = default_port,
+ .timeout = timeout,
+ .reconnects_counter = reconnects_counter,
+ .connected_to = connected_to,
+ .connected_to_size = connected_to_size,
+ .sock = -1,
+ };
+
+ foreach_entry_in_connection_string(destination, connect_to_one_of_urls_callback, &t);
+
+ return t.sock;
}
@@ -1087,12 +1142,11 @@ int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *clien
if (getnameinfo((struct sockaddr *)&sadr, addrlen, client_ip, (socklen_t)ipsize,
client_port, (socklen_t)portsize, NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
error("LISTENER: cannot getnameinfo() on received client connection.");
- strncpyz(client_ip, "UNKNOWN", ipsize - 1);
- strncpyz(client_port, "UNKNOWN", portsize - 1);
+ strncpyz(client_ip, "UNKNOWN", ipsize);
+ strncpyz(client_port, "UNKNOWN", portsize);
}
if (!strcmp(client_ip, "127.0.0.1") || !strcmp(client_ip, "::1")) {
- strncpy(client_ip, "localhost", ipsize);
- client_ip[ipsize - 1] = '\0';
+ strncpyz(client_ip, "localhost", ipsize);
}
#ifdef __FreeBSD__
@@ -1107,8 +1161,7 @@ int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *clien
case AF_UNIX:
debug(D_LISTENER, "New UNIX domain web client from %s on socket %d.", client_ip, fd);
// set the port - certain versions of libc return garbage on unix sockets
- strncpy(client_port, "UNIX", portsize);
- client_port[portsize - 1] = '\0';
+ strncpyz(client_port, "UNIX", portsize);
break;
case AF_INET:
diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h
index a40d801ddd..2c78bb9744 100644
--- a/libnetdata/socket/socket.h
+++ b/libnetdata/socket/socket.h
@@ -10,19 +10,22 @@
#endif
typedef enum web_client_acl {
- WEB_CLIENT_ACL_NONE = 0,
- WEB_CLIENT_ACL_NOCHECK = 0,
- WEB_CLIENT_ACL_DASHBOARD = 1 << 0,
- WEB_CLIENT_ACL_REGISTRY = 1 << 1,
- WEB_CLIENT_ACL_BADGE = 1 << 2,
- WEB_CLIENT_ACL_MGMT = 1 << 3,
- WEB_CLIENT_ACL_STREAMING = 1 << 4,
- WEB_CLIENT_ACL_NETDATACONF = 1 << 5,
+ WEB_CLIENT_ACL_NONE = 0,
+ WEB_CLIENT_ACL_NOCHECK = 0,
+ WEB_CLIENT_ACL_DASHBOARD = 1 << 0,
+ WEB_CLIENT_ACL_REGISTRY = 1 << 1,
+ WEB_CLIENT_ACL_BADGE = 1 << 2,
+ WEB_CLIENT_ACL_MGMT = 1 << 3,
+ WEB_CLIENT_ACL_STREAMING = 1 << 4,
+ WEB_CLIENT_ACL_NETDATACONF = 1 << 5,
WEB_CLIENT_ACL_SSL_OPTIONAL = 1 << 6,
- WEB_CLIENT_ACL_SSL_FORCE = 1 << 7,
- WEB_CLIENT_ACL_SSL_DEFAULT = 1 << 8
+ WEB_CLIENT_ACL_SSL_FORCE = 1 << 7,
+ WEB_CLIENT_ACL_SSL_DEFAULT = 1 << 8,
+ WEB_CLIENT_ACL_ACLK = 1 << 9,
} WEB_CLIENT_ACL;
+#define WEB_CLIENT_ACL_ALL 0xFFFF
+
#define web_client_can_access_dashboard(w) ((w)->acl & WEB_CLIENT_ACL_DASHBOARD)
#define web_client_can_access_registry(w) ((w)->acl & WEB_CLIENT_ACL_REGISTRY)
#define web_client_can_access_badges(w) ((w)->acl & WEB_CLIENT_ACL_BADGE)
@@ -54,9 +57,12 @@ extern char *strdup_client_description(int family, const char *protocol, const c
extern int listen_sockets_setup(LISTEN_SOCKETS *sockets);
extern void listen_sockets_close(LISTEN_SOCKETS *sockets);
+extern void foreach_entry_in_connection_string(const char *destination, bool (*callback)(char *entry, void *data), void *data);
+extern int connect_to_this_ip46(int protocol, int socktype, const char *host, uint32_t scope_id, const char *service, struct timeval *timeout);
extern int connect_to_this(const char *definition, int default_port, struct timeval *timeout);
extern int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size);
-int connect_to_this_ip46(int protocol, int socktype, const char *host, uint32_t scope_id, const char *service, struct timeval *timeout);
+extern int connect_to_one_of_urls(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size);
+
#ifdef ENABLE_HTTPS
extern ssize_t recv_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout);