672 lines
20 KiB
C
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], ©_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 |