This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
stellar-stellar/infra/monitor/monitor_utils.c
2024-11-07 18:30:58 +08:00

672 lines
20 KiB
C

#include "sds/sds.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/in.h>
#include <threads.h>
#ifdef __cplusplus
extern "C"
{
#endif
#include "monitor_private.h"
#include "stellar/session.h"
#include "tuple/tuple.h"
struct stm_key_value *stm_cmd_key_value_new(void)
{
struct stm_key_value *kv = (struct stm_key_value *)calloc(1, sizeof(struct stm_key_value));
kv->tuple_num = 0;
kv->tuple = NULL;
return kv;
}
void stm_cmd_key_value_append(struct stm_key_value **kv, const char *key, const char *value)
{
if (NULL == *kv)
{
*kv = stm_cmd_key_value_new();
}
struct stm_key_value *new_kv = *kv;
new_kv->tuple = (struct stm_key_value_tuple *)realloc(new_kv->tuple, (new_kv->tuple_num + 1) * sizeof(struct stm_key_value_tuple));
new_kv->tuple[new_kv->tuple_num].key = strdup(key);
new_kv->tuple[new_kv->tuple_num].value = strdup(value);
new_kv->tuple_num++;
*kv = new_kv;
}
void stm_cmd_key_value_free(struct stm_key_value *kv)
{
if (NULL == kv)
{
return;
}
for (int i = 0; i < kv->tuple_num; i++)
{
FREE(kv->tuple[i].key);
FREE(kv->tuple[i].value);
}
FREE(kv->tuple);
FREE(kv);
}
void stm_cmd_request_free(struct stm_cmd_request *req)
{
FREE(req);
}
int stm_strncasecmp_exactly(const char *s1, const char *s2, size_t n)
{
if (NULL == s1 || NULL == s2)
{
return -1;
}
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
if (len1 != len2 || len1 != n)
{
return -1;
}
return strncasecmp(s1, s2, n);
}
const char *stm_session_state_ntop(enum session_state state)
{
switch (state)
{
case SESSION_STATE_OPENING:
return "opening";
break;
case SESSION_STATE_ACTIVE:
return "active";
break;
case SESSION_STATE_CLOSING:
return "closing";
break;
default:
break;
}
return "unknown";
}
const char *stm_session_flow_dir_ntop(uint32_t dir)
{
if (SESSION_SEEN_C2S_FLOW == dir)
{
return "C2S";
}
else if (SESSION_SEEN_S2C_FLOW == dir)
{
return "S2C";
}
else if ((SESSION_SEEN_C2S_FLOW | SESSION_SEEN_S2C_FLOW) == dir)
{
return "BIDIRECTION";
}
return "UNKNOWN";
}
int stm_time_range_pton(const char *time_str, time_t now, time_t time_range[2])
{
const char *delim = "-";
char *save_ptr;
const char *time_str_min_example = "last-1-days";
if (NULL == time_str || NULL == time_range)
{
return -1;
}
if (strlen(time_str) < strlen(time_str_min_example))
{
return -1;
}
char *local_time_str = strdup(time_str);
char *fix_prefix = strtok_r(local_time_str, delim, &save_ptr);
if (stm_strncasecmp_exactly(fix_prefix, "last", 4) != 0)
{
free(local_time_str);
return -1;
}
char *number_string = strtok_r(NULL, delim, &save_ptr);
if (NULL == number_string)
{
free(local_time_str);
return -1;
}
long number = strtol(number_string, NULL, 10);
if (number <= 0 || number > 3600)
{
free(local_time_str);
return -1;
}
char *unit_string = strtok_r(NULL, delim, &save_ptr);
if (NULL == unit_string)
{
free(local_time_str);
return -1;
}
long long multiple_second = 1;
if (stm_strncasecmp_exactly(unit_string, "seconds", 7) == 0)
{
multiple_second = 1;
}
else if (stm_strncasecmp_exactly(unit_string, "minutes", 7) == 0)
{
multiple_second = 60;
}
else if (stm_strncasecmp_exactly(unit_string, "hours", 5) == 0)
{
multiple_second = 60 * 60;
}
else if (stm_strncasecmp_exactly(unit_string, "days", 4) == 0)
{
multiple_second = 60 * 60 * 24;
}
else
{
free(local_time_str);
return -1;
}
if ((long long)now < number * multiple_second)
{
time_range[0] = 0;
}
else
{
time_range[0] = now - number * multiple_second;
}
time_range[1] = now;
while (strtok_r(NULL, delim, &save_ptr))
{
}
free(local_time_str);
return 0;
}
int stm_time_in_range(time_t t, const time_t time_range[2])
{
if (NULL == time_range)
{
return 0;
}
if (t >= time_range[0] && t <= time_range[1])
{
return 1;
}
return 0;
}
uint32_t stm_inet_pton(const char *ipstr, void *ipv4_value, void *ipv6_value)
{
unsigned int tmp_ipv4;
struct in6_addr tmp_ipv6;
if (NULL == ipstr || NULL == ipv4_value || NULL == ipv6_value)
{
return 0;
}
if (inet_pton(AF_INET, ipstr, &tmp_ipv4) == 1)
{
memcpy(ipv4_value, &tmp_ipv4, sizeof(int));
return AF_INET;
}
if (inet_pton(AF_INET6, ipstr, &tmp_ipv6) == 1)
{
memcpy(ipv6_value, &tmp_ipv6, sizeof(struct in6_addr));
return AF_INET6;
}
return 0;
}
/**
* Calculate a 128-bit mask given a network prefix.
*/
void in6_addr_mask(struct in6_addr *mask, uint8_t bits)
{
for (uint8_t i = 0; i < sizeof(mask->s6_addr); i++)
{
mask->s6_addr[i] = bits ? (uint8_t)0xFF << (8 - (bits > 8 ? 8 : bits)) : 0;
if (bits < 8)
bits = 0;
else
bits -= 8;
}
}
/**
* Calculate the first address in a network given a mask.
*/
void in6_addr_network(struct in6_addr *network, const struct in6_addr *addr, const struct in6_addr *mask)
{
for (uint8_t i = 0; i < sizeof(network->s6_addr); i++)
{
network->s6_addr[i] = addr->s6_addr[i] & mask->s6_addr[i];
}
}
/**
* Calculate the last address in a network given a mask.
*/
void in6_addr_end(struct in6_addr *end, const struct in6_addr *addr, const struct in6_addr *mask)
{
for (uint8_t i = 0; i < sizeof(end->s6_addr); i++)
{
end->s6_addr[i] = (addr->s6_addr[i] & mask->s6_addr[i]) | ~mask->s6_addr[i];
}
}
int stm_ipv4_cidr_to_range(uint32_t ipaddr, uint32_t ipmask, uint32_t iprange[2])
{
uint32_t network_addr = ipaddr & ipmask;
uint32_t broadcast_addr = (ipaddr & ipmask) | ~ipmask;
iprange[0] = network_addr;
iprange[1] = broadcast_addr;
return 0;
}
int stm_ipv6_cidr_to_range(const struct in6_addr *ipaddr, const struct in6_addr *ipmask, struct in6_addr iprange[2])
{
struct in6_addr network_addr = {};
struct in6_addr broadcast_addr = {};
in6_addr_network(&network_addr, ipaddr, ipmask);
in6_addr_end(&broadcast_addr, ipaddr, ipmask);
memcpy(&iprange[0], &network_addr, sizeof(struct in6_addr));
memcpy(&iprange[1], &broadcast_addr, sizeof(struct in6_addr));
return 0;
}
uint32_t stm_ip_cidr_pton(const char *ipcidr, void *ipv4_value, void *ipv4_mask, void *ipv6_value, void *ipv6_mask)
{
int ipver = 0;
const char *delim = "/";
char *save_ptr;
if (NULL == ipcidr || NULL == ipv4_value || NULL == ipv4_mask || NULL == ipv6_value || NULL == ipv6_mask)
{
return 0;
}
if (strchr(ipcidr, '/') == NULL)
{
return 0;
}
char *local_ip_cidr = strdup(ipcidr);
char *ipaddr = strtok_r(local_ip_cidr, delim, &save_ptr);
ipver = stm_inet_pton(ipaddr, ipv4_value, ipv6_value);
if (ipver != AF_INET && ipver != AF_INET6)
{
free(local_ip_cidr);
return 0;
}
char *cidr = strtok_r(NULL, delim, &save_ptr);
if (NULL == cidr)
{
free(local_ip_cidr);
return 0;
}
int cidr_num = atoi(cidr);
if (ipver == AF_INET)
{
if (cidr_num <= 0 || cidr_num > 32)
{
free(local_ip_cidr);
return 0;
}
uint32_t mask = 0;
for (int i = 0; i < cidr_num; i++)
{
mask |= (uint32_t)1 << (31 - i);
}
mask = ntohl(mask);
memcpy(ipv4_mask, &mask, sizeof(int));
}
else if (ipver == AF_INET6)
{
if (cidr_num <= 0 || cidr_num > 128)
{
free(local_ip_cidr);
return -1;
}
struct in6_addr mask = {};
for (int i = 0; i < cidr_num; i++)
{
mask.s6_addr[i / 8] |= 1 << (7 - i % 8);
}
memcpy(ipv6_mask, &mask, sizeof(struct in6_addr));
}
while (strtok_r(NULL, delim, &save_ptr))
;
free(local_ip_cidr);
return ipver;
}
void stm_mem_fill_rand(char *buf, size_t len, unsigned char range_min, unsigned char range_max)
{
unsigned char *p = (unsigned char *)buf;
for (size_t i = 0; i < len; i++)
{
p[i] = (rand() % range_min + 1) + (range_max - range_min);
}
}
int stm_string_isdigit(const char *str)
{
if (NULL == str || strlen(str) == 0)
{
return 0;
}
for (size_t i = 0; i < strlen(str); i++)
{
if (!isdigit(str[i]))
{
return 0;
}
}
return 1;
}
/*
* input("hello, world!", "hello", "hi", &output) -> output = "hi, world!"
* return: the number of sub string have been replaced.
*/
int stm_replace_str(const char *raw, const char *search, const char *replace, char **outline)
{
const char *p = NULL;
int search_len = strlen(search);
int replace_len = strlen(replace);
int raw_len = strlen(raw);
p = strstr(raw, search);
if (p == NULL)
{
return 0;
}
const char *remain_ptr = p + search_len;
char *new_line = (char *)calloc(1, replace_len + (raw_len - search_len) + 1);
strcpy(new_line, replace);
strcpy(new_line + replace_len, remain_ptr);
*outline = new_line;
return 1;
}
int stm_timeout(struct timeval start, struct timeval end, int timeout_sec)
{
return (end.tv_sec * 1000 * 1000 + end.tv_usec) - (start.tv_sec * 1000 * 1000 + start.tv_usec) >= timeout_sec * 1000 * 1000;
}
struct monitor_reply *monitor_reply_nil(void)
{
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
reply->type = MONITOR_REPLY_NIL;
reply->http_code = HTTP_OK;
reply->http_reason = "OK";
return reply;
}
/* string should without "\r\n", will add "\r\n" in monitor_reply_to_string() */
struct monitor_reply *monitor_reply_new_string(const char *format, ...)
{
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
reply->type = MONITOR_REPLY_STRING;
reply->http_code = HTTP_OK;
reply->http_reason = "OK";
va_list ap;
va_start(ap, format);
reply->str = sdscatvprintf(sdsempty(), format, ap);
reply->len = strlen(reply->str);
va_end(ap);
return reply;
}
/* string should without "\r\n", will add "\r\n" in monitor_reply_to_string() */
struct monitor_reply *monitor_reply_new_error(const char *format, ...)
{
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
reply->type = MONITOR_REPLY_ERROR;
reply->http_code = HTTP_BADREQUEST;
reply->http_reason = "ERROR";
va_list ap;
va_start(ap, format);
reply->str = sdscatvprintf(sdsempty(), format, ap);
reply->len = strlen(reply->str);
va_end(ap);
return reply;
}
struct monitor_reply *monitor_reply_new_status(const char *format, ...)
{
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
reply->type = MONITOR_REPLY_STATUS;
reply->http_code = HTTP_OK;
reply->http_reason = "OK";
va_list ap;
va_start(ap, format);
reply->str = sdscatvprintf(sdsempty(), format, ap);
reply->len = strlen(reply->str);
va_end(ap);
return reply;
}
struct monitor_reply *monitor_reply_new_integer(long long integer)
{
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
reply->type = MONITOR_REPLY_INTEGER;
reply->http_code = HTTP_OK;
reply->http_reason = "OK";
reply->integer = integer;
return reply;
}
struct monitor_reply *monitor_reply_new_double(double dval)
{
struct monitor_reply *reply = CALLOC(struct monitor_reply, 1);
reply->type = MONITOR_REPLY_DOUBLE;
reply->http_code = HTTP_OK;
reply->http_reason = "OK";
reply->dval = dval;
return reply;
}
sds monitor_reply_to_string(const struct monitor_reply *reply)
{
sds res = sdsempty();
switch (reply->type)
{
case MONITOR_REPLY_INTEGER:
res = sdscatprintf(res, "(integer) %lld\r\n", reply->integer);
break;
case MONITOR_REPLY_DOUBLE:
res = sdscatprintf(res, "(double) %f\r\n", reply->dval);
break;
case MONITOR_REPLY_STRING:
case MONITOR_REPLY_STATUS:
res = sdscatlen(res, reply->str, reply->len);
res = sdscat(res, "\r\n");
break;
case MONITOR_REPLY_NIL:
res = sdscat(res, "(nil)\r\n");
break;
case MONITOR_REPLY_ERROR:
res = sdscatprintf(res, "(error) %s\r\n", reply->str);
break;
default:
break;
}
return res;
}
void monitor_reply_free(struct monitor_reply *reply)
{
switch (reply->type)
{
case MONITOR_REPLY_STRING:
case MONITOR_REPLY_ERROR:
case MONITOR_REPLY_STATUS:
sdsfree(reply->str);
reply->str = NULL;
break;
default:
break;
}
free(reply);
}
static struct monitor_cli_args *monitor_util_search_cmd_args(const char *opt_name, const struct monitor_cli_args cli_args[], size_t cli_args_array_size)
{
if (NULL == cli_args)
{
return NULL;
}
for (size_t i = 0; i < cli_args_array_size; i++)
{
if (stm_strncasecmp_exactly(opt_name, cli_args[i].short_opt, strlen(cli_args[i].short_opt)) == 0 || stm_strncasecmp_exactly(opt_name, cli_args[i].long_opt, strlen(cli_args[i].long_opt)) == 0)
{
return (struct monitor_cli_args *)&cli_args[i];
}
}
return NULL;
}
sds monitor_util_copy_arg_value(int argc, const char *argv[], int *copy_args_num)
{
sds res = sdsempty();
int num = 0;
for (int i = 0; i < argc; i++)
{
if ((strncmp(argv[i], "-", 1) == 0) || (strncmp(argv[i], "--", 2) == 0))
{
break;
}
else
{
res = sdscat(res, argv[i]);
if ((i + 1 < argc) && (strncmp(argv[i + 1], "-", 1) != 0) && (strncmp(argv[i + 1], "--", 2) != 0)) // not the last one
{
res = sdscat(res, " ");
}
num++;
}
}
*copy_args_num = num;
return res;
}
int monitor_util_parse_cmd_args(int argc, const char *argv[], struct monitor_cli_args cli_args[], size_t cli_args_array_size)
{
if (argc <= 0 || NULL == argv || NULL == cli_args)
{
return -1;
}
int parse_stage = 0; // 0: arg; 1: value
struct monitor_cli_args *one_arg = NULL;
for (int i = 1; i < argc;) // skip program self name
{
if (0 == parse_stage)
{
one_arg = monitor_util_search_cmd_args(argv[i], cli_args, cli_args_array_size);
if (NULL == one_arg)
{
fprintf(stderr, "unknown option: %s\n", argv[i]);
return -1;
}
if (one_arg->require_arg_value && (i + 1 >= argc))
{
fprintf(stderr, "option requires value: %s\n", argv[i]);
return -1;
}
parse_stage = 1;
i += 1;
}
else
{
if (NULL == one_arg)
{
fprintf(stderr, "unknown option: %s\n", argv[i]);
return -1;
}
int copy_args_num = 0;
if (one_arg->value_is_multi_words)
{
one_arg->value = monitor_util_copy_arg_value(argc - i, &argv[i], &copy_args_num);
}
else
{
one_arg->value = sdsnew(argv[i]);
copy_args_num = 1;
}
i += copy_args_num;
parse_stage = 0;
one_arg = NULL;
}
}
return 0;
}
sds stm_config_print(const struct stellar_monitor_config *config)
{
sds res = sdsempty();
res = sdscatprintf(res, "cli_request_timeout: %ds, ", config->cli_request_timeout);
res = sdscatprintf(res, "connection_idle_timeout: %ds, ", config->connection_idle_timeout);
res = sdscatprintf(res, "listen_ip_addr: %s, ", config->listen_ipaddr);
res = sdscatprintf(res, "listen_port %u, ", config->listen_port_host_order);
res = sdscatprintf(res, "data_link_bind_port: %u, ", config->data_link_bind_port_host_order);
res = sdscatprintf(res, "stat_output_path: %s, ", config->output_path);
res = sdscatprintf(res, "stat_output_interval_ms: %dms ", config->output_interval_ms);
return res;
}
char *stm_http_url_encode(const char *originalText)
{
// allocate memory for the worst possible case (all characters need to be encoded)
char *encodedText = (char *)malloc(sizeof(char) * strlen(originalText) * 3 + 1);
const char *hex = "0123456789abcdef";
int pos = 0;
for (size_t i = 0; i < strlen(originalText); i++)
{
if (('a' <= originalText[i] && originalText[i] <= 'z') || ('A' <= originalText[i] && originalText[i] <= 'Z') || ('0' <= originalText[i] && originalText[i] <= '9'))
{
encodedText[pos++] = originalText[i];
}
else if (originalText[i] == ' ')
{
encodedText[pos++] = '%';
encodedText[pos++] = hex[originalText[i] >> 4];
encodedText[pos++] = hex[originalText[i] & 15];
}
/* todo: other characters ? */
else
{
encodedText[pos++] = originalText[i];
}
}
encodedText[pos] = '\0';
return encodedText;
}
const char *stm_get0_readable_session_addr(const struct tuple6 *addr, char *addr_buf, size_t max_buf_len)
{
tuple4_to_str((struct tuple4 *)addr, addr_buf, max_buf_len);
return addr_buf;
}
#ifdef __cplusplus
}
#endif