Compare commits

...

14 Commits

Author SHA1 Message Date
Jan Vidar Krey
aa26052479 Misc plugin work
* Fix crash when unloading plugins.
* Fixed plugin loader and plugin unload handler.
* Added a new example plugin: mod_logging which logs users logging in and out of the hub to stdout.
2010-06-29 15:40:19 +02:00
Jan Vidar Krey
ccaa4860b4 Started working on the plugin configuration and loader code.
The example plugin works, but does not do anything.
2010-06-13 19:34:20 +02:00
Jan Vidar Krey
7218011449 Added missing plugin file. 2010-06-07 15:42:38 +02:00
Jan Vidar Krey
baeba01835 Added file_plugins directive to configuration file. 2010-06-06 16:32:43 +02:00
Jan Vidar Krey
07d4e4470c More work on the plugin API. 2010-06-06 16:19:59 +02:00
Jan Vidar Krey
ed53034ad5 Added plugin loader code, merely a wrapper around dlopen() and friends. 2010-05-30 23:33:06 +02:00
Jan Vidar Krey
fc8965f1c5 Started working on an authentication plugin API. 2010-05-30 13:03:03 +02:00
Jan Vidar Krey
b3ed3a5526 Fix a silly invalid read operation 2010-05-28 15:23:44 +02:00
Jan Vidar Krey
1480b7e9c0 Fix bug #133 - Kqueue backend crash (BSD/OSX) 2010-05-28 15:22:33 +02:00
Jan Vidar Krey
50fde1c5c8 Updated the broadcast command to not send the message back to the user that sent it, but
instead summarize the number of users that received it.
2010-05-26 09:38:14 +02:00
Jan Vidar Krey
77faac0494 Fixed some minor issues found during code review. 2010-05-25 22:47:30 +02:00
Jan Vidar Krey
473ff0e159 Fix static buffer overrun - Thanks BlackSonar. 2010-05-25 22:23:40 +02:00
Jan Vidar Krey
62333c5f39 Update the command to send a PM originating from the operator/admin that sent it. 2010-05-25 22:21:56 +02:00
Jan Vidar Krey
44860c8477 Fix bug #131 - Missing escape handling for # in config files.
Added support for escaping stuff in the configuration file parser.
2010-05-25 19:43:32 +02:00
28 changed files with 970 additions and 83 deletions

View File

@@ -12,6 +12,7 @@ RANLIB := ranlib
CFLAGS += -pipe -Wall
USE_SSL ?= NO
USE_BIGENDIAN ?= AUTO
USE_PLUGINS ?= YES
BITS ?= AUTO
SILENT ?= YES
TERSE ?= NO
@@ -42,6 +43,7 @@ UHUB_PREFIX ?= c:/uhub/
CFLAGS += -mno-cygwin
LDFLAGS += -mno-cygwin
BIN_EXT ?= .exe
USE_PLUGINS := NO
else
DESTDIR ?= /
UHUB_CONF_DIR ?= $(DESTDIR)/etc/uhub
@@ -118,6 +120,12 @@ CFLAGS += -DSSL_SUPPORT
LDLIBS += -lssl
endif
ifeq ($(USE_PLUGINS),YES)
CFLAGS += -DPLUGIN_SUPPORT
LDLIBS += -ldl
endif
GIT_VERSION=$(shell git describe --tags 2>/dev/null || echo "")
GIT_REVISION=$(shell git show --abbrev-commit 2>/dev/null | head -n 1 | cut -f 2 -d " " || echo "")
OLD_REVISION=$(shell grep GIT_REVISION revision.h 2>/dev/null | cut -f 3 -d " " | tr -d "\"")
@@ -138,6 +146,8 @@ libuhub_SOURCES := \
src/core/route.c \
src/core/user.c \
src/core/usermanager.c \
src/core/plugininvoke.c \
src/core/pluginloader.c \
src/network/backend.c \
src/network/connection.c \
src/network/epoll.c \
@@ -183,6 +193,13 @@ autotest_SOURCES := \
autotest_OBJECTS = autotest.o
plugin_example_SOURCES := src/plugins/mod_example.c
plugin_example_TARGET := $(plugin_example_SOURCES:.c=.so)
plugin_logging_SOURCES := src/plugins/mod_logging.c
plugin_logging_TARGET := $(plugin_example_SOURCES:.c=.so)
# Source to objects
libuhub_OBJECTS := $(libuhub_SOURCES:.c=.o)
libadc_client_OBJECTS := $(libadc_client_SOURCES:.c=.o)
@@ -193,14 +210,23 @@ adcrush_OBJECTS := $(adcrush_SOURCES:.c=.o)
admin_OBJECTS := $(admin_SOURCES:.c=.o)
all_OBJECTS := $(libuhub_OBJECTS) $(uhub_OBJECTS) $(adcrush_OBJECTS) $(autotest_OBJECTS) $(admin_OBJECTS) $(libadc_common_OBJECTS) $(libadc_client_OBJECTS)
all_plugins := $(plugin_example_TARGET) $(plugin_logging_TARGET)
uhub_BINARY=uhub$(BIN_EXT)
adcrush_BINARY=adcrush$(BIN_EXT)
admin_BINARY=uhub-admin$(BIN_EXT)
autotest_BINARY=autotest/test$(BIN_EXT)
ifeq ($(USE_PLUGINS),YES)
all_OBJECTS += $(plugins)
endif
.PHONY: revision.h.tmp
%.so: %.c
$(MSG_CC) $(CC) -shared -fPIC -o $@ $< $(CFLAGS)
%.o: %.c version.h revision.h
$(MSG_CC) $(CC) -c $(CFLAGS) -o $@ $<

View File

@@ -756,7 +756,7 @@ char* adc_msg_get_argument(struct adc_message* cmd, int offset)
else
{
argument = hub_strdup(&start[1]);
if (argument && argument[strlen(argument)-1] == '\n')
if (argument && *argument && argument[strlen(argument)-1] == '\n')
argument[strlen(argument)-1] = 0;
}

View File

@@ -1,6 +1,6 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, Jan Vidar Krey
* Copyright (C) 2007-2010, Jan Vidar Krey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -176,14 +176,10 @@ static int check_cmd_addr(const char* cmd, struct linked_list* list, char* line,
static int acl_parse_line(char* line, int line_count, void* ptr_data)
{
char* pos;
struct acl_handle* handle = (struct acl_handle*) ptr_data;
int ret;
if ((pos = strchr(line, '#')) != NULL)
{
pos[0] = 0;
}
strip_off_ini_line_comments(line, line_count);
line = strip_white_space(line);
if (!*line)

View File

@@ -1,6 +1,6 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, Jan Vidar Krey
* Copyright (C) 2007-2010, Jan Vidar Krey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View File

@@ -412,11 +412,48 @@ static int command_whoip(struct hub_info* hub, struct hub_user* user, struct hub
static int command_broadcast(struct hub_info* hub, struct hub_user* user, struct hub_command* cmd)
{
struct adc_message* command = adc_msg_construct(ADC_CMD_IMSG, strlen((cmd->message + 12)) + 6);
adc_msg_add_argument(command, (cmd->message + 12));
size_t offset = 12;
#if USE_OLD_BROADCAST_STYLE
struct adc_message* command = adc_msg_construct(ADC_CMD_IMSG, strlen((cmd->message + offset)) + 6);
adc_msg_add_argument(command, (cmd->message + offset));
route_to_all(hub, command);
adc_msg_free(command);
return 0;
#else
size_t message_len = strlen(cmd->message + offset);
struct adc_message* command = 0;
char pm_flag[7] = "PM";
char from_sid[5];
char buffer[128];
size_t recipients = 0;
memcpy(from_sid, sid_to_string(user->id.sid), sizeof(from_sid));
memcpy(pm_flag + 2, from_sid, sizeof(from_sid));
struct hub_user* target = (struct hub_user*) list_get_first(hub->users->list);
while (target)
{
if (target != user)
{
recipients++;
command = adc_msg_construct(ADC_CMD_DMSG, message_len + 23);
if (!command)
break;
adc_msg_add_argument(command, from_sid);
adc_msg_add_argument(command, sid_to_string(target->id.sid));
adc_msg_add_argument(command, (cmd->message + offset));
adc_msg_add_argument(command, pm_flag);
route_to_user(hub, target, command);
adc_msg_free(command);
}
target = (struct hub_user*) list_get_next(hub->users->list);
}
snprintf(buffer, sizeof(buffer), "*** %s: Delivered to " PRINTF_SIZE_T " user%s", cmd->prefix, recipients, (recipients != 1 ? "s" : ""));
return 0;
#endif
}
static int command_history(struct hub_info* hub, struct hub_user* user, struct hub_command* cmd)

View File

@@ -85,10 +85,7 @@ static int config_parse_line(char* line, int line_count, void* ptr_data)
char* data;
struct hub_config* config = (struct hub_config*) ptr_data;
if ((pos = strchr(line, '#')) != NULL)
{
pos[0] = 0;
}
strip_off_ini_line_comments(line, line_count);
if (!*line) return 0;

View File

@@ -490,6 +490,19 @@
]]></example>
</option>
<option name="file_plugins" type="file" default="">
<short>Plugin configuration file</short>
<description><![CDATA[
Plugin configuration file.
]]></description>
<since>0.3.3</since>
<example><![CDATA[
<p>
file_plugins = "/etc/uhub/plugins.conf"
</p>
]]></example>
</option>
<option name="msg_hub_full" type="message" default="Hub is full" >
<description><![CDATA[This will be sent if the hub is full]]></description>
<since>0.2.0</since>

View File

@@ -47,6 +47,7 @@ void config_defaults(struct hub_config* config)
config->file_motd = hub_strdup("");
config->file_acl = hub_strdup("");
config->file_rules = hub_strdup("");
config->file_plugins = hub_strdup("");
config->msg_hub_full = hub_strdup("Hub is full");
config->msg_hub_disabled = hub_strdup("Hub is disabled");
config->msg_hub_registered_users_only = hub_strdup("Hub is for registered users only");
@@ -545,6 +546,16 @@ static int apply_config(struct hub_config* config, char* key, char* data, int li
return 0;
}
if (!strcmp(key, "file_plugins"))
{
if (!apply_string(key, data, &config->file_plugins, (char*) ""))
{
LOG_ERROR("Configuration parse error on line %d", line_count);
return -1;
}
return 0;
}
if (!strcmp(key, "msg_hub_full"))
{
if (!apply_string(key, data, &config->msg_hub_full, (char*) ""))
@@ -932,6 +943,8 @@ void free_config(struct hub_config* config)
hub_free(config->file_rules);
hub_free(config->file_plugins);
hub_free(config->msg_hub_full);
hub_free(config->msg_hub_disabled);
@@ -1143,6 +1156,9 @@ void dump_config(struct hub_config* config, int ignore_defaults)
if (!ignore_defaults || strcmp(config->file_rules, "") != 0)
fprintf(stdout, "file_rules = \"%s\"\n", config->file_rules);
if (!ignore_defaults || strcmp(config->file_plugins, "") != 0)
fprintf(stdout, "file_plugins = \"%s\"\n", config->file_plugins);
if (!ignore_defaults || strcmp(config->msg_hub_full, "Hub is full") != 0)
fprintf(stdout, "msg_hub_full = \"%s\"\n", config->msg_hub_full);

View File

@@ -47,6 +47,7 @@ struct hub_config
char* file_motd; /*<<< File containing the 'message of the day (default: ) */
char* file_acl; /*<<< File containing access control lists (default: ) */
char* file_rules; /*<<< File containing hub rules (default: ) */
char* file_plugins; /*<<< Plugin configuration file (default: ) */
char* msg_hub_full; /*<<< "Hub is full" */
char* msg_hub_disabled; /*<<< "Hub is disabled" */
char* msg_hub_registered_users_only; /*<<< "Hub is for registered users only" */

View File

@@ -257,9 +257,15 @@ int hub_handle_chat_message(struct hub_info* hub, struct hub_user* u, struct adc
int relay = 1;
int offset;
if (!message || !user_is_logged_in(u))
if (!message)
return 0;
if (!user_is_logged_in(u))
{
hub_free(message);
return 0;
}
if ((cmd->cache[0] == 'B') && (message[0] == '!' || message[0] == '+'))
{
/*
@@ -480,11 +486,11 @@ void hub_send_flood_warning(struct hub_info* hub, struct hub_user* u, const char
adc_msg_add_argument(msg, "110");
adc_msg_add_argument(msg, tmp);
hub_free(tmp);
}
route_to_user(hub, u, msg);
user_flag_set(u, flag_flood);
adc_msg_free(msg);
route_to_user(hub, u, msg);
user_flag_set(u, flag_flood);
adc_msg_free(msg);
}
}
static void hub_event_dispatcher(void* callback_data, struct event_data* message)
@@ -800,6 +806,45 @@ void hub_shutdown_service(struct hub_info* hub)
g_hub = 0;
}
#ifdef PLUGIN_SUPPORT
void hub_plugins_load(struct hub_info* hub)
{
if (!hub->config->file_plugins || !*hub->config->file_plugins)
return;
hub->plugins = hub_malloc_zero(sizeof(struct uhub_plugins));
if (!hub->plugins)
return;
if (plugin_initialize(hub->config, hub->plugins) < 0)
{
hub_free(hub->plugins);
hub->plugins = 0;
return;
}
}
void hub_plugins_unload(struct hub_info* hub)
{
if (!hub->plugins || !hub->plugins->loaded)
{
return;
}
struct uhub_plugin_handle* plugin = (struct uhub_plugin_handle*) list_get_first(hub->plugins->loaded);
while (plugin)
{
plugin_unload(plugin);
plugin = (struct uhub_plugin_handle*) list_get_next(hub->plugins->loaded);
}
list_destroy(hub->plugins->loaded);
hub_free(hub->plugins->plugin_dir);
hub_free(hub->plugins);
hub->plugins = 0;
}
#endif
void hub_set_variables(struct hub_info* hub, struct acl_handle* acl)
{
int fd, ret;
@@ -874,6 +919,10 @@ void hub_set_variables(struct hub_info* hub, struct acl_handle* acl)
hub_free(tmp);
}
#ifdef PLUGIN_SUPPORT
hub_plugins_load(hub);
#endif
hub->status = (hub->config->hub_enabled ? hub_status_running : hub_status_disabled);
hub_free(server);
}
@@ -881,6 +930,10 @@ void hub_set_variables(struct hub_info* hub, struct acl_handle* acl)
void hub_free_variables(struct hub_info* hub)
{
#ifdef PLUGIN_SUPPORT
hub_plugins_unload(hub);
#endif
adc_msg_free(hub->command_info);
adc_msg_free(hub->command_banner);
@@ -944,7 +997,7 @@ void hub_send_status(struct hub_info* hub, struct hub_user* user, enum status_me
struct adc_message* cmd = adc_msg_construct(ADC_CMD_ISTA, 6);
struct adc_message* qui = adc_msg_construct(ADC_CMD_IQUI, 512);
char code[4];
char buf[250];
char buf[256];
const char* text = 0;
const char* flag = 0;
char* escaped_text = 0;

View File

@@ -114,6 +114,10 @@ struct hub_info
struct linked_list* chat_history; /* Chat history */
struct linked_list* logout_info; /* Log of people logging out. */
#ifdef PLUGIN_SUPPORT
struct uhub_plugins* plugins;
#endif
#ifdef SSL_SUPPORT
SSL_METHOD* ssl_method;
SSL_CTX* ssl_ctx;

View File

@@ -18,6 +18,7 @@
*/
#include "uhub.h"
#include "plugin_api/handle.h"
static void log_user_login(struct hub_user* u)
{
@@ -67,6 +68,10 @@ void on_login_success(struct hub_info* hub, struct hub_user* u)
/* Print log message */
log_user_login(u);
#ifdef PLUGIN_SUPPORT
plugin_log_user_login_success(hub, u);
#endif
/* Announce new user to all connected users */
if (user_is_logged_in(u))
route_info_message(hub, u);
@@ -109,6 +114,11 @@ void on_logout_user(struct hub_info* hub, struct hub_user* user)
{
const char* reason = user_get_quit_reason_string(user->quit_reason);
log_user_logout(user, reason);
#ifdef PLUGIN_SUPPORT
plugin_log_user_logout(hub, user);
#endif
hub_logout_log(hub, user);
}

View File

@@ -175,8 +175,10 @@ void net_on_accept(struct net_connection* con, int event, void *arg)
struct hub_info* hub = (struct hub_info*) arg;
struct hub_probe* probe = 0;
struct ip_addr_encap ipaddr;
const char* addr;
int server_fd = net_con_get_sd(con);
#ifdef PLUGIN_SUPPORT
plugin_st status;
#endif
for (;;)
{
@@ -194,19 +196,18 @@ void net_on_accept(struct net_connection* con, int event, void *arg)
}
}
addr = ip_convert_to_string(&ipaddr);
/* FIXME: Should have a plugin log this */
LOG_TRACE("Got connection from %s", addr);
/* FIXME: A plugin should perform this check: is IP banned? */
if (acl_is_ip_banned(hub->acl, addr))
#ifdef PLUGIN_SUPPORT
status = plugin_check_ip_early(hub, &ipaddr);
if (status == st_deny)
{
LOG_INFO("Denied [%s] (IP banned)", addr);
plugin_log_connection_denied(hub, &ipaddr);
net_close(fd);
continue;
}
plugin_log_connection_accepted(hub, &ipaddr);
#endif
probe = probe_create(hub, fd, &ipaddr);
if (!probe)
{

89
src/core/plugininvoke.c Normal file
View File

@@ -0,0 +1,89 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2010, Jan Vidar Krey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "uhub.h"
#include "plugin_api/handle.h"
#define PLUGIN_INVOKE(HUB, FUNCNAME, CODE) \
if (HUB->plugins && HUB->plugins->loaded) \
{ \
struct uhub_plugin_handle* plugin = (struct uhub_plugin_handle*) list_get_first(HUB->plugins->loaded); \
while (plugin) \
{ \
if (plugin->funcs.FUNCNAME) \
CODE \
plugin = (struct uhub_plugin_handle*) list_get_next(HUB->plugins->loaded); \
} \
}
plugin_st plugin_check_ip_early(struct hub_info* hub, struct ip_addr_encap* addr)
{
plugin_st status = st_default;
PLUGIN_INVOKE(hub, login_check_ip_early, {
status = plugin->funcs.login_check_ip_early(addr);
if (status != st_default)
break;
});
return status;
}
plugin_st plugin_check_ip_late(struct hub_info* hub, struct ip_addr_encap* addr)
{
plugin_st status = st_default;
PLUGIN_INVOKE(hub, login_check_ip_late, {
status = plugin->funcs.login_check_ip_late(addr);
if (status != st_default)
break;
});
return status;
}
void plugin_log_connection_accepted(struct hub_info* hub, struct ip_addr_encap* ipaddr)
{
const char* addr = ip_convert_to_string(ipaddr);
LOG_TRACE("Got connection from %s", addr);
}
void plugin_log_connection_denied(struct hub_info* hub, struct ip_addr_encap* ipaddr)
{
const char* addr = ip_convert_to_string(ipaddr);
LOG_INFO("Denied connection from %s", addr);
}
void plugin_log_user_login_success(struct hub_info* hub, struct hub_user* user)
{
}
void plugin_log_user_login_error(struct hub_info* hub, struct hub_user* user)
{
}
void plugin_log_user_logout(struct hub_info* hub, struct hub_user* user)
{
}
void convert_user_to_plugin_user(struct plugin_user* puser, struct hub_user* user)
{
puser->sid = user->id.sid;
puser->nick = user->id.nick;
puser->cid = user->id.cid;
puser->addr = user->id.addr;
}

46
src/core/plugininvoke.h Normal file
View File

@@ -0,0 +1,46 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2010, Jan Vidar Krey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef HAVE_UHUB_PLUGIN_INVOKE_H
#define HAVE_UHUB_PLUGIN_INVOKE_H
#include "uhub.h"
#include "plugin_api/handle.h"
#ifdef PLUGIN_SUPPORT
struct hub_info;
struct ip_addr_encap;
void plugin_log_connection_accepted(struct hub_info* hub, struct ip_addr_encap* addr);
void plugin_log_connection_denied(struct hub_info* hub, struct ip_addr_encap* addr);
void plugin_log_user_login_success(struct hub_info* hub, struct hub_user* user);
void plugin_log_user_login_error(struct hub_info* hub, struct hub_user* user);
void plugin_log_user_logout(struct hub_info* hub, struct hub_user* user);
plugin_st plugin_check_ip_early(struct hub_info* hub, struct ip_addr_encap* addr);
plugin_st plugin_check_ip_late(struct hub_info* hub, struct ip_addr_encap* addr);
plugin_st plugin_check_nickname_valid(struct hub_info* hub, const char* nick);
plugin_st plugin_check_nickname_reserved(struct hub_info* hub, const char* nick);
#endif
#endif // HAVE_UHUB_PLUGIN_INVOKE_H

187
src/core/pluginloader.c Normal file
View File

@@ -0,0 +1,187 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2010, Jan Vidar Krey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "uhub.h"
#ifdef PLUGIN_SUPPORT
#include "plugin_api/handle.h"
struct uhub_plugin* plugin_open(const char* filename)
{
LOG_TRACE("plugin_open: \"%s\"", filename);
#ifdef HAVE_DLOPEN
struct uhub_plugin* plugin = (struct uhub_plugin*) hub_malloc_zero(sizeof(struct uhub_plugin));
if (!plugin)
{
return 0;
}
plugin->handle = dlopen(filename, RTLD_LAZY);
if (!plugin->handle)
{
LOG_ERROR("Unable to open plugin %s: %s", filename, dlerror());
hub_free(plugin);
return 0;
}
return plugin;
#else
return 0;
#endif
}
void plugin_close(struct uhub_plugin* plugin)
{
#ifdef HAVE_DLOPEN
dlclose(plugin->handle);
hub_free(plugin);
#endif
}
void* plugin_lookup_symbol(struct uhub_plugin* plugin, const char* symbol)
{
#ifdef HAVE_DLOPEN
void* addr = dlsym(plugin->handle, symbol);
return addr;
#else
return 0;
#endif
}
struct uhub_plugin_handle* plugin_load(const char* filename, const char* config)
{
plugin_register_f register_f;
plugin_unregister_f unregister_f;
int ret;
struct uhub_plugin_handle* handle = hub_malloc_zero(sizeof(struct uhub_plugin_handle));
struct uhub_plugin* plugin = plugin_open(filename);
if (!plugin)
return NULL;
if (!handle)
{
plugin_close(plugin);
return NULL;
}
handle->handle = plugin;
register_f = plugin_lookup_symbol(plugin, "plugin_register");
unregister_f = plugin_lookup_symbol(plugin, "plugin_unregister");
if (register_f && unregister_f)
{
ret = register_f(handle, config);
if (ret == 0)
{
if (handle->plugin_api_version == PLUGIN_API_VERSION && handle->plugin_funcs_size == sizeof(struct plugin_funcs))
{
LOG_INFO("Loaded plugin: %s: %s, version %s.", filename, handle->name, handle->version);
LOG_TRACE("Plugin API version: %d (func table size: " PRINTF_SIZE_T ")", handle->plugin_api_version, handle->plugin_funcs_size);
plugin->unregister = unregister_f;
return handle;
}
else
{
LOG_ERROR("Unable to load plugin: %s - API version mistmatch", filename);
}
}
else
{
LOG_ERROR("Unable to load plugin: %s - Failed to initialize", filename);
}
}
plugin_close(plugin);
hub_free(handle);
return NULL;
}
void plugin_unload(struct uhub_plugin_handle* plugin)
{
plugin->handle->unregister(plugin);
plugin_close(plugin->handle);
}
static int plugin_parse_line(char* line, int line_count, void* ptr_data)
{
struct uhub_plugins* handle = (struct uhub_plugins*) ptr_data;
char* pos;
strip_off_ini_line_comments(line, line_count);
line = strip_white_space(line);
if (!*line)
return 0;
LOG_TRACE("plugin: parse line %d: \"%s\"", line_count, line);
// Set plugin directory.
pos = strstr(line, "plugin_directory");
if (pos && is_white_space(line[(pos - line) + strlen("plugin_directory")]))
{
if (handle->plugin_dir)
hub_free(handle->plugin_dir);
handle->plugin_dir = strdup(strip_white_space(pos + strlen("plugin_directory") + 1));
return 0;
}
// Load plugin
pos = strstr(line, "plugin");
if (pos && is_white_space(line[(pos - line) + strlen("plugin")]))
{
char* data = strip_white_space(pos + strlen("plugin") + 1);
if (*data)
{
LOG_TRACE("Load plugin: \"%s\"", data);
struct uhub_plugin_handle* plugin = plugin_load(data, "");
if (plugin)
{
list_append(handle->loaded, plugin);
return 0;
}
}
}
return -1;
}
int plugin_initialize(struct hub_config* config, struct uhub_plugins* handle)
{
int ret;
handle->loaded = list_create();
if (!handle->loaded)
return -1;
if (config)
{
if (!*config->file_plugins)
return 0;
ret = file_read_lines(config->file_plugins, handle, &plugin_parse_line);
if (ret == -1)
return -1;
}
return 0;
}
#endif /* PLUGIN_SUPPORT */

62
src/core/pluginloader.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2010, Jan Vidar Krey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef HAVE_UHUB_PLUGIN_LOADER_H
#define HAVE_UHUB_PLUGIN_LOADER_H
#include "plugin_api/handle.h"
#ifdef PLUGIN_SUPPORT
struct hub_config;
struct linked_list;
struct uhub_plugin_handle;
struct uhub_plugin
{
#ifdef HAVE_DLOPEN
void* handle;
#endif
plugin_unregister_f unregister;
};
struct uhub_plugins
{
struct linked_list* loaded;
char* plugin_dir;
};
// High level plugin loader ode
extern struct uhub_plugin_handle* plugin_load(const char* filename, const char* config);
extern void plugin_unload(struct uhub_plugin_handle* plugin);
// extern void plugin_unload(struct uhub_plugin_handle*);
extern int plugin_initialize(struct hub_config* config, struct uhub_plugins* handle);
// Low level plugin loader code (used internally)
extern struct uhub_plugin* plugin_open(const char* filename);
extern void plugin_close(struct uhub_plugin*);
extern void* plugin_lookup_symbol(struct uhub_plugin*, const char* symbol);
#endif /* PLUGIN_SUPPORT */
#endif /* HAVE_UHUB_PLUGIN_LOADER_H */

View File

@@ -92,9 +92,10 @@ static void probe_net_event(struct net_connection* con, int events, void *arg)
net_con_ssl_handshake(con, net_con_ssl_mode_server, probe->hub->ssl_ctx);
return;
}
#endif
#else
probe_destroy(probe);
return;
#endif
}
}
}

View File

@@ -79,11 +79,8 @@ int net_backend_init()
timeout_queue_initialize(&g_backend->timeout_queue, g_backend->now, 120); /* FIXME: max 120 secs! */
g_backend->cleaner = net_cleanup_initialize(g_backend->common.max);
for (n = 0; n < sizeof(net_backend_init_funcs); n++)
for (n = 0; net_backend_init_funcs[n]; n++)
{
if (!net_backend_init_funcs[n])
break;
g_backend->data = net_backend_init_funcs[n](&g_backend->handler, &g_backend->common);
if (g_backend->data)
{

View File

@@ -32,19 +32,29 @@ struct net_connection_kqueue
NET_CON_STRUCT_COMMON
struct kevent ev_r;
struct kevent ev_w;
int change;
};
struct net_backend_kqueue
{
int kqfd;
struct net_connection_kqueue** conns;
struct kevent** changes;
size_t nchanges;
struct kevent* changes;
int* change_list;
size_t change_list_len;
struct kevent events[KQUEUE_EVBUFFER];
struct net_backend_common* common;
};
#define CHANGE_ACTION_ADD 0x0001
#define CHANGE_ACTION_MOD 0x0002
#define CHANGE_ACTION_DEL 0x0004
#define CHANGE_OP_WANT_READ 0x0100
#define CHANGE_OP_WANT_WRITE 0x0200
static void net_backend_set_handlers(struct net_backend_handler* handler);
static void add_change(struct net_backend_kqueue* backend, struct net_connection_kqueue* con, int actions);
static size_t create_change_list(struct net_backend_kqueue* backend);
const char* net_backend_name_kqueue()
{
@@ -56,12 +66,13 @@ int net_backend_poll_kqueue(struct net_backend* data, int ms)
int res;
struct timespec tspec = { 0, };
struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data;
size_t changes;
tspec.tv_sec = (ms / 1000);
tspec.tv_nsec = ((ms % 1000) * 1000000);
res = kevent(backend->kqfd, *backend->changes, backend->nchanges, backend->events, KQUEUE_EVBUFFER, &tspec);
backend->nchanges = 0;
changes = create_change_list(backend);
res = kevent(backend->kqfd, backend->changes, changes, backend->events, KQUEUE_EVBUFFER, &tspec);
if (res == -1 && errno == EINTR)
return 0;
@@ -76,11 +87,13 @@ void net_backend_process_kqueue(struct net_backend* data, int res)
for (n = 0; n < res; n++)
{
struct net_connection_kqueue* con = (struct net_connection_kqueue*) backend->events[n].udata;
int ev = -1;
if (backend->events[n].filter == EVFILT_READ) ev = NET_EVENT_READ;
else if (backend->events[n].filter == EVFILT_WRITE) ev = NET_EVENT_WRITE;
if (con)
if (con && con->sd >= 0 && backend->conns[con->sd])
{
int ev = 0;
if (backend->events[n].filter == EVFILT_READ) ev = NET_EVENT_READ;
else if (backend->events[n].filter == EVFILT_WRITE) ev = NET_EVENT_WRITE;
net_con_callback((struct net_connection*) con, ev);
}
}
}
@@ -102,58 +115,37 @@ void net_con_initialize_kqueue(struct net_backend* data, struct net_connection*
void net_con_backend_add_kqueue(struct net_backend* data, struct net_connection* con_, int events)
{
unsigned short flags_r = EV_ADD;
unsigned short flags_w = EV_ADD;
struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data;
struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_;
int operation;
backend->conns[con->sd] = con;
if (events & NET_EVENT_READ)
flags_r |= EV_ENABLE;
else
flags_r |= EV_DISABLE;
operation = CHANGE_ACTION_ADD;
EV_SET(&con->ev_r, con->sd, EVFILT_READ, flags_r, 0, 0, con);
backend->changes[backend->nchanges++] = &con->ev_r;
if (events & NET_EVENT_READ)
operation |= CHANGE_OP_WANT_READ;
if (events & NET_EVENT_WRITE)
flags_w |= EV_ENABLE;
else
flags_w |= EV_DISABLE;
operation |= CHANGE_OP_WANT_WRITE;
EV_SET(&con->ev_w, con->sd, EVFILT_WRITE, flags_w, 0, 0, con);
backend->changes[backend->nchanges++] = &con->ev_w;
add_change(backend, con, operation);
}
void net_con_backend_mod_kqueue(struct net_backend* data, struct net_connection* con_, int events)
{
unsigned short flags_r = 0;
unsigned short flags_w = 0;
struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data;
struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_;
if (events & NET_EVENT_READ)
flags_r |= EV_ENABLE;
else
flags_r |= EV_DISABLE;
int operation = CHANGE_ACTION_ADD;
if (!(con->ev_r.flags & flags_r))
{
EV_SET(&con->ev_r, con->sd, EVFILT_READ, flags_r, 0, 0, con);
backend->changes[backend->nchanges++] = &con->ev_r;
}
if (events & NET_EVENT_READ)
operation |= CHANGE_OP_WANT_READ;
if (events & NET_EVENT_WRITE)
flags_r |= EV_ENABLE;
else
flags_r |= EV_DISABLE;
operation |= CHANGE_OP_WANT_WRITE;
if (!(con->ev_w.flags & flags_w))
{
EV_SET(&con->ev_w, con->sd, EVFILT_WRITE, flags_w, 0, 0, con);
backend->changes[backend->nchanges++] = &con->ev_w;
}
add_change(backend, con, operation);
}
void net_con_backend_del_kqueue(struct net_backend* data, struct net_connection* con_)
@@ -162,11 +154,8 @@ void net_con_backend_del_kqueue(struct net_backend* data, struct net_connection*
struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_;
/* No need to remove it from the kqueue filter, the kqueue man page says
it is automatically removed when the descriptor is closed. */
EV_SET(&con->ev_r, con->sd, EVFILT_READ, EV_DELETE, 0, 0, 0);
backend->changes[backend->nchanges++] = &con->ev_r;
EV_SET(&con->ev_w, con->sd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
backend->changes[backend->nchanges++] = &con->ev_w;
it is automatically removed when the descriptor is closed... */
add_change(backend, con, CHANGE_ACTION_DEL);
// Unmap the socket descriptor.
backend->conns[con->sd] = 0;
@@ -178,6 +167,7 @@ void net_backend_shutdown_kqueue(struct net_backend* data)
close(backend->kqfd);
hub_free(backend->conns);
hub_free(backend->changes);
hub_free(backend->change_list);
hub_free(backend);
}
@@ -197,8 +187,8 @@ struct net_backend* net_backend_init_kqueue(struct net_backend_handler* handler,
}
backend->conns = hub_malloc_zero(sizeof(struct net_connection_kqueue*) * common->max);
backend->conns = hub_malloc_zero(sizeof(struct net_connection_kqueue*) * common->max);
backend->changes = hub_malloc_zero(sizeof(struct kevent*) * common->max * 2);
backend->changes = hub_malloc_zero(sizeof(struct kevent) * common->max * 2);
backend->change_list = hub_malloc_zero(sizeof(int) * common->max);
backend->common = common;
net_backend_set_handlers(handler);
@@ -218,4 +208,71 @@ static void net_backend_set_handlers(struct net_backend_handler* handler)
handler->con_del = net_con_backend_del_kqueue;
}
static void add_change(struct net_backend_kqueue* backend, struct net_connection_kqueue* con, int actions)
{
if (actions && !con->change)
{
backend->change_list[backend->change_list_len++] = con->sd;
con->change = actions;
}
}
static size_t create_change_list(struct net_backend_kqueue* backend)
{
size_t n = 0;
size_t changes = 0;
int sd;
struct net_connection_kqueue* con;
unsigned short flags_r = 0;
unsigned short flags_w = 0;
for (; n < backend->change_list_len; n++)
{
sd = backend->change_list[n];
con = backend->conns[sd];
if (con)
{
flags_r = 0;
flags_w = 0;
if (con->change & CHANGE_ACTION_ADD)
{
flags_r |= EV_ADD;
flags_w |= EV_ADD;
}
if (con->change & CHANGE_OP_WANT_READ)
flags_r |= EV_ENABLE;
else
flags_r |= EV_DISABLE;
if (con->change & CHANGE_OP_WANT_WRITE)
flags_w |= EV_ENABLE;
else
flags_w |= EV_DISABLE;
if (con->ev_r.flags != flags_r)
{
EV_SET(&con->ev_r, sd, EVFILT_READ, flags_r, 0, 0, con);
memcpy(&backend->changes[changes++], &con->ev_r, sizeof(struct kevent));
}
if (con->ev_w.flags != flags_w)
{
EV_SET(&con->ev_w, sd, EVFILT_WRITE, flags_w, 0, 0, con);
memcpy(&backend->changes[changes++], &con->ev_w, sizeof(struct kevent));
}
con->change = 0;
}
else
{
EV_SET(&backend->changes[changes++], sd, EVFILT_READ, EV_DELETE, 0, 0, 0);
EV_SET(&backend->changes[changes++], sd, EVFILT_READ, EV_DELETE, 0, 0, 0);
}
}
backend->change_list_len = 0;
return changes;
}
#endif /* USE_KQUEUE */

153
src/plugin_api/handle.h Normal file
View File

@@ -0,0 +1,153 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2010, Jan Vidar Krey
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef HAVE_UHUB_PLUGIN_HANDLE_H
#define HAVE_UHUB_PLUGIN_HANDLE_H
#include "system.h"
#include "util/ipcalc.h"
#define PLUGIN_API_VERSION 0
#ifndef MAX_NICK_LEN
#define MAX_NICK_LEN 64
#endif
#ifndef MAX_PASS_LEN
#define MAX_PASS_LEN 64
#endif
struct ip_addr_encap;
struct plugin_user
{
unsigned int sid;
const char* nick;
const char* cid;
struct ip_addr_encap addr;
};
enum plugin_status
{
st_default = 0, /* Use default */
st_allow = 1, /* Allow action */
st_deny = -1, /* Deny action */
};
typedef enum plugin_status plugin_st;
enum auth_credentials
{
auth_cred_none, /**<<< "User has no credentials (not yet logged in)" */
auth_cred_bot, /**<<< "User is a robot" */
auth_cred_guest, /**<<< "User is a guest (unregistered user)" */
auth_cred_user, /**<<< "User is identified as a registered user" */
auth_cred_operator, /**<<< "User is identified as a hub operator" */
auth_cred_super, /**<<< "User is a super user" (not used) */
auth_cred_link, /**<<< "User is a link (not used currently)" */
auth_cred_admin, /**<<< "User is identified as a hub administrator/owner" */
};
struct auth_info
{
char nickname[MAX_NICK_LEN+1];
char password[MAX_PASS_LEN+1];
enum auth_credentials credentials;
};
typedef plugin_st (*on_chat_msg_t)(struct plugin_user* from, const char* message);
typedef plugin_st (*on_private_msg_t)(struct plugin_user* from, struct plugin_user* to, const char* message);
typedef plugin_st (*on_search_t)(struct plugin_user* from, const char* data);
typedef plugin_st (*on_p2p_connect_t)(struct plugin_user* from, struct plugin_user* to);
typedef plugin_st (*on_p2p_revconnect_t)(struct plugin_user* from, struct plugin_user* to);
typedef void (*on_user_connect_t)(struct ip_addr_encap*);
typedef void (*on_user_login_t)(struct plugin_user*);
typedef void (*on_user_logout_t)(struct plugin_user*);
typedef void (*on_user_nick_change_t)(struct plugin_user*, const char* new_nick);
typedef plugin_st (*on_change_nick_t)(struct plugin_user*, const char* new_nick);
typedef plugin_st (*on_check_ip_early_t)(struct ip_addr_encap*);
typedef plugin_st (*on_check_ip_late_t)(struct ip_addr_encap*);
typedef plugin_st (*on_validate_nick_t)(const char* nick);
typedef plugin_st (*on_validate_cid_t)(const char* cid);
typedef int (*auth_get_user_t)(const char* nickname, struct auth_info* info);
typedef plugin_st (*auth_register_user_t)(struct auth_info* user);
typedef plugin_st (*auth_update_user_t)(struct auth_info* user);
typedef plugin_st (*auth_delete_user_t)(struct auth_info* user);
struct plugin_funcs
{
// Users logging in and out
on_user_connect_t on_user_connect; /* A user has connected to the hub */
on_user_login_t on_user_login; /* A user has successfully logged in to the hub */
on_user_logout_t on_user_logout; /* A user has logged out of the hub (was previously logged in) */
on_user_nick_change_t on_user_nick_change; /* A user has changed nickname */
// Activity events (can be intercepted and refused by a plugin)
on_chat_msg_t on_chat_msg; /* A public chat message is about to be sent (can be intercepted) */
on_private_msg_t on_private_msg; /* A public chat message is about to be sent (can be intercepted) */
on_search_t on_search; /* A search is about to be sent (can be intercepted) */
on_p2p_connect_t on_p2p_connect; /* A user is about to connect to another user (can be intercepted) */
on_p2p_revconnect_t on_p2p_revconnect; /* A user is about to connect to another user (can be intercepted) */
// Authentication actions.
auth_get_user_t auth_get_user; /* Get authentication info from plugin */
auth_register_user_t auth_register_user; /* Register user */
auth_update_user_t auth_update_user; /* Update a registered user */
auth_delete_user_t auth_delete_user; /* Delete a registered user */
// Login check functions
on_check_ip_early_t login_check_ip_early;
on_check_ip_late_t login_check_ip_late;
};
struct uhub_plugin_handle
{
struct uhub_plugin* handle; /* Must NOT be modified by the plugin */
const char* name; /* plugin name */
const char* version; /* plugin version */
const char* description; /* plugin description */
void* ptr; /* Plugin specific data */
size_t plugin_api_version; /* Plugin API version */
size_t plugin_funcs_size; /* Size of the plugin funcs */
struct plugin_funcs funcs;
};
/**
* Implemented by the plugin.
*
* @param handle[out] Sets all information by the plugin
* @param config A configuration string
* @return 0 on success, -1 on error.
*/
extern int plugin_register(struct uhub_plugin_handle* handle, const char* config);
/**
* @return 0 on success, -1 on error.
*/
extern int plugin_unregister(struct uhub_plugin_handle*);
typedef int (*plugin_register_f)(struct uhub_plugin_handle* handle, const char* config);
typedef int (*plugin_unregister_f)(struct uhub_plugin_handle*);
#endif /* HAVE_UHUB_PLUGIN_HANDLE_H */

28
src/plugins/mod_example.c Normal file
View File

@@ -0,0 +1,28 @@
/**
* This is a minimal example plugin for uhub.
*/
// #include "uhub.h"
#include "plugin_api/handle.h"
int plugin_register(struct uhub_plugin_handle* plugin, const char* config)
{
plugin->name = "Example plugin";
plugin->version = "1.0";
plugin->description = "A simple example plugin";
plugin->ptr = NULL;
plugin->plugin_api_version = PLUGIN_API_VERSION;
plugin->plugin_funcs_size = sizeof(struct plugin_funcs);
memset(&plugin->funcs, 0, sizeof(struct plugin_funcs));
puts("plugin register");
return 0;
}
int plugin_unregister(struct uhub_plugin_handle* plugin)
{
/* No need to do anything! */
puts("plugin unregister");
return 0;
}

57
src/plugins/mod_logging.c Normal file
View File

@@ -0,0 +1,57 @@
/**
* This is a minimal example plugin for uhub.
*/
// #include "uhub.h"
#include "plugin_api/handle.h"
struct ip_addr_encap;
plugin_st log_connect(struct ip_addr_encap* addr)
{
return st_default;
}
void log_user_login(struct plugin_user* user)
{
printf("login: \"%s\"\n", user->nick);
}
void log_user_logout(struct plugin_user* user)
{
printf("logout: \"%s\"\n", user->nick);
}
plugin_st log_change_nick(struct plugin_user* user, const char* new_nick)
{
printf("\"%s\" -> \"%s\"\n", user->nick, new_nick);
return st_default;
}
int plugin_register(struct uhub_plugin_handle* plugin, const char* config)
{
plugin->name = "Logging plugin";
plugin->version = "1.0";
plugin->description = "Logs users entering and leaving the hub.";
plugin->ptr = NULL;
plugin->plugin_api_version = PLUGIN_API_VERSION;
plugin->plugin_funcs_size = sizeof(struct plugin_funcs);
memset(&plugin->funcs, 0, sizeof(struct plugin_funcs));
plugin->funcs.on_connect = log_connect;
plugin->funcs.on_user_login = log_user_login;
plugin->funcs.on_user_logout = log_user_logout;
plugin->funcs.on_user_change_nick = log_change_nick;
puts("* plugin register");
return 0;
}
int plugin_unregister(struct uhub_plugin_handle* plugin)
{
/* No need to do anything! */
puts("* plugin unregister");
return 0;
}

View File

@@ -89,6 +89,8 @@
#include <pwd.h>
#include <sys/resource.h>
#define HAVE_STRNDUP
#define HAVE_DLOPEN
#include <dlfcn.h>
#ifndef __HAIKU__
#define HAVE_MEMMEM
#endif

View File

@@ -45,6 +45,7 @@
#define MAX_CID_LEN 39
#define MAX_NICK_LEN 64
#define MAX_PASS_LEN 64
#define MAX_UA_LEN 32
#define TIGERSIZE 24
@@ -80,10 +81,13 @@ extern "C" {
#include "core/user.h"
#include "core/usermanager.h"
#include "core/route.h"
#include "core/pluginloader.h"
#include "core/hub.h"
#include "core/commands.h"
#include "core/inf.h"
#include "core/hubevent.h"
#include "core/plugininvoke.h"
#include "core/pluginloader.h"
#ifdef __cplusplus
}

View File

@@ -400,3 +400,51 @@ const char* get_timestamp(time_t now)
sprintf(ts, "[%02d:%02d]", t->tm_hour, t->tm_min);
return ts;
}
void strip_off_ini_line_comments(char* line, int line_count)
{
char* p = line;
char* out = line;
int backslash = 0;
if (!*line)
return;
for (; *p; p++)
{
if (!backslash)
{
if (*p == '\\')
{
backslash = 1;
}
else if (*p == '#')
{
*out = '\0';
out++;
break;
}
else
{
*out = *p;
out++;
}
}
else
{
if (*p == '\\' || *p == '#' || *p == '\"')
{
*out = *p;
out++;
}
else
{
LOG_WARN("Invalid backslash escape on line %d", line_count);
*out = *p;
out++;
}
backslash = 0;
}
}
*out = '\0';
}

View File

@@ -33,10 +33,12 @@ extern int is_valid_base32_char(char c);
extern void base32_encode(const unsigned char* buffer, size_t len, char* result);
extern void base32_decode(const char* src, unsigned char* dst, size_t len);
extern char* strip_white_space(char* string);
extern void strip_off_ini_line_comments(char* line, int line_count);
extern int file_read_lines(const char* file, void* data, file_line_handler_t handler);
extern const char* uhub_itoa(int val);
extern const char* uhub_ulltoa(uint64_t val);