#include "sds/sds.h" #include #include #include #include #include #include #include #include #include #include #include #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], ©_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