Reorganized sources slightly.

This commit is contained in:
Jan Vidar Krey
2009-07-25 20:05:27 +02:00
parent e281f61472
commit 36a07e3f7e
52 changed files with 72 additions and 69 deletions

583
src/core/auth.c Normal file
View File

@@ -0,0 +1,583 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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"
#define ACL_ADD_USER(S, L, V) do { ret = check_cmd_user(S, V, L, line, line_count); if (ret != 0) return ret; } while(0)
#define ACL_ADD_BOOL(S, L) do { ret = check_cmd_bool(S, L, line, line_count); if (ret != 0) return ret; } while(0)
#define ACL_ADD_ADDR(S, L) do { ret = check_cmd_addr(S, L, line, line_count); if (ret != 0) return ret; } while(0)
const char* get_user_credential_string(enum user_credentials cred)
{
switch (cred)
{
case cred_none: return "none";
case cred_bot: return "bot";
case cred_guest: return "guest";
case cred_user: return "user";
case cred_operator: return "operator";
case cred_super: return "super";
case cred_admin: return "admin";
case cred_link: return "link";
}
return "";
};
static int check_cmd_bool(const char* cmd, struct linked_list* list, char* line, int line_count)
{
char* data;
char* data_extra;
if (!strncmp(line, cmd, strlen(cmd)))
{
data = &line[strlen(cmd)];
data_extra = 0;
data[0] = '\0';
data++;
data = strip_white_space(data);
if (!*data)
{
hub_log(log_fatal, "ACL parse error on line %d", line_count);
return -1;
}
list_append(list, hub_strdup(data));
hub_log(log_debug, "ACL: Deny access for: '%s' (%s)", data, cmd);
return 1;
}
return 0;
}
static int check_cmd_user(const char* cmd, int status, struct linked_list* list, char* line, int line_count)
{
char* data;
char* data_extra;
struct user_access_info* info = 0;
if (!strncmp(line, cmd, strlen(cmd)))
{
data = &line[strlen(cmd)];
data_extra = 0;
data[0] = '\0';
data++;
data = strip_white_space(data);
if (!*data)
{
hub_log(log_fatal, "ACL parse error on line %d", line_count);
return -1;
}
info = hub_malloc_zero(sizeof(struct user_access_info));
if (!info)
{
hub_log(log_error, "ACL parse error. Out of memory!");
return -1;
}
if (strncmp(cmd, "user_", 5) == 0)
{
data_extra = strrchr(data, ':');
if (data_extra)
{
data_extra[0] = 0;
data_extra++;
}
}
info->username = hub_strdup(data);
info->password = data_extra ? hub_strdup(data_extra) : 0;
info->status = status;
list_append(list, info);
hub_log(log_debug, "ACL: Added user '%s' (%s)", info->username, get_user_credential_string(info->status));
return 1;
}
return 0;
}
static void add_ip_range(struct linked_list* list, struct ip_ban_record* info)
{
char buf1[INET6_ADDRSTRLEN+1];
char buf2[INET6_ADDRSTRLEN+1];
if (info->lo.af == AF_INET)
{
net_address_to_string(AF_INET, &info->lo.internal_ip_data.in.s_addr, buf1, INET6_ADDRSTRLEN);
net_address_to_string(AF_INET, &info->hi.internal_ip_data.in.s_addr, buf2, INET6_ADDRSTRLEN);
}
else if (info->lo.af == AF_INET6)
{
net_address_to_string(AF_INET6, &info->lo.internal_ip_data.in6, buf1, INET6_ADDRSTRLEN);
net_address_to_string(AF_INET6, &info->hi.internal_ip_data.in6, buf2, INET6_ADDRSTRLEN);
}
hub_log(log_debug, "ACL: Deny access for: %s-%s", buf1, buf2);
list_append(list, info);
}
static int check_ip_range(const char* lo, const char* hi, struct ip_ban_record* info)
{
int ret1, ret2;
if ((ip_is_valid_ipv4(lo) && ip_is_valid_ipv4(hi)) ||
(ip_is_valid_ipv6(lo) && ip_is_valid_ipv6(hi)))
{
ret1 = ip_convert_to_binary(lo, &info->lo);
ret2 = ip_convert_to_binary(hi, &info->hi);
if (ret1 == -1 || ret2 == -1 || ret1 != ret2)
{
return -1;
}
return 0;
}
return -1;
}
static int check_ip_mask(const char* text_addr, int bits, struct ip_ban_record* info)
{
hub_log(log_debug, "ACL: Deny access for: %s/%d", text_addr, bits);
if (ip_is_valid_ipv4(text_addr) ||
ip_is_valid_ipv6(text_addr))
{
struct ip_addr_encap addr;
struct ip_addr_encap mask1;
struct ip_addr_encap mask2;
int af = ip_convert_to_binary(text_addr, &addr); /* 192.168.1.2 */
int maxbits = af == AF_INET6 ? 128 : 32;
ip_mask_create_left(af, bits, &mask1); /* 255.255.255.0 */
ip_mask_create_right(af, maxbits - bits, &mask2); /* 0.0.0.255 */
ip_mask_apply_AND(&addr, &mask1, &info->lo); /* 192.168.1.0 */
ip_mask_apply_OR(&info->lo, &mask2, &info->hi); /* 192.168.1.255 */
return 0;
}
return -1;
}
static int check_cmd_addr(const char* cmd, struct linked_list* list, char* line, int line_count)
{
char* data1;
char* data2;
struct ip_ban_record* info = 0;
int cidr_bits = 0;
if (!strncmp(line, cmd, strlen(cmd)))
{
data1 = &line[strlen(cmd)];
data2 = 0;
data1[0] = '\0';
data1++;
data1 = strip_white_space(data1);
if (!*data1)
{
hub_log(log_fatal, "ACL parse error on line %d", line_count);
return -1;
}
info = hub_malloc_zero(sizeof(struct ip_ban_record));
if (!info)
{
hub_log(log_error, "ACL parse error. Out of memory!");
return -1;
}
/* Extract IP-range */
data2 = strrchr(data1, '-');
if (data2)
{
cidr_bits = -1;
data2[0] = 0;
data2++;
if (check_ip_range(data1, data2, info) == -1)
{
hub_free(info);
return 0;
}
add_ip_range(list, info);
return 1;
}
else
{
/* Extract IP-bitmask */
data2 = strrchr(data1, '/');
if (data2)
{
data2[0] = 0;
data2++;
cidr_bits = uhub_atoi(data2);
}
else
{
cidr_bits = 128;
}
if (check_ip_mask(data1, cidr_bits, info) == -1)
{
hub_free(info);
return 0;
}
add_ip_range(list, info);
return 1;
}
}
return 0;
}
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;
}
if (!*line)
return 0;
#ifdef ACL_DEBUG
hub_log(log_trace, "acl_parse_line(): '%s'", line);
#endif
line = strip_white_space(line);
if (!*line)
{
hub_log(log_fatal, "ACL parse error on line %d", line_count);
return -1;
}
#ifdef ACL_DEBUG
hub_log(log_trace, "acl_parse_line: '%s'", line);
#endif
ACL_ADD_USER("bot", handle->users, cred_bot);
ACL_ADD_USER("user_admin", handle->users, cred_admin);
ACL_ADD_USER("user_super", handle->users, cred_super);
ACL_ADD_USER("user_op", handle->users, cred_operator);
ACL_ADD_USER("user_reg", handle->users, cred_user);
ACL_ADD_USER("link", handle->users, cred_link);
ACL_ADD_BOOL("deny_nick", handle->users_denied);
ACL_ADD_BOOL("ban_nick", handle->users_banned);
ACL_ADD_BOOL("ban_cid", handle->cids);
ACL_ADD_ADDR("deny_ip", handle->networks);
ACL_ADD_ADDR("nat_ip", handle->nat_override);
hub_log(log_error, "Unknown ACL command on line %d: '%s'", line_count, line);
return -1;
}
int acl_initialize(struct hub_config* config, struct acl_handle* handle)
{
int ret;
memset(handle, 0, sizeof(struct acl_handle));
handle->users = list_create();
handle->users_denied = list_create();
handle->users_banned = list_create();
handle->cids = list_create();
handle->networks = list_create();
handle->nat_override = list_create();
if (!handle->users || !handle->cids || !handle->networks || !handle->users_denied || !handle->users_banned || !handle->nat_override)
{
hub_log(log_fatal, "acl_initialize: Out of memory");
list_destroy(handle->users);
list_destroy(handle->users_denied);
list_destroy(handle->users_banned);
list_destroy(handle->cids);
list_destroy(handle->networks);
list_destroy(handle->nat_override);
return -1;
}
if (config)
{
if (!*config->file_acl) return 0;
ret = file_read_lines(config->file_acl, handle, &acl_parse_line);
if (ret == -1)
return -1;
}
return 0;
}
static void acl_free_access_info(void* ptr)
{
struct user_access_info* info = (struct user_access_info*) ptr;
if (info)
{
hub_free(info->username);
hub_free(info->password);
hub_free(info);
}
}
static void acl_free_ip_info(void* ptr)
{
struct access_info* info = (struct access_info*) ptr;
if (info)
{
hub_free(info);
}
}
int acl_shutdown(struct acl_handle* handle)
{
if (handle->users)
{
list_clear(handle->users, &acl_free_access_info);
list_destroy(handle->users);
}
if (handle->users_denied)
{
list_clear(handle->users_denied, &hub_free);
list_destroy(handle->users_denied);
}
if (handle->users_banned)
{
list_clear(handle->users_banned, &hub_free);
list_destroy(handle->users_banned);
}
if (handle->cids)
{
list_clear(handle->cids, &hub_free);
list_destroy(handle->cids);
}
if (handle->networks)
{
list_clear(handle->networks, &acl_free_ip_info);
list_destroy(handle->networks);
}
if (handle->nat_override)
{
list_clear(handle->nat_override, &acl_free_ip_info);
list_destroy(handle->nat_override);
}
memset(handle, 0, sizeof(struct acl_handle));
return 0;
}
struct user_access_info* acl_get_access_info(struct acl_handle* handle, const char* name)
{
struct user_access_info* info = (struct user_access_info*) list_get_first(handle->users);
while (info)
{
if (strcasecmp(info->username, name) == 0)
{
return info;
}
info = (struct user_access_info*) list_get_next(handle->users);
}
return NULL;
}
#define STR_LIST_CONTAINS(LIST, STR) \
char* str = (char*) list_get_first(LIST); \
while (str) \
{ \
if (strcasecmp(str, STR) == 0) \
return 1; \
str = (char*) list_get_next(LIST); \
} \
return 0
int acl_is_cid_banned(struct acl_handle* handle, const char* data)
{
if (!handle) return 0;
STR_LIST_CONTAINS(handle->cids, data);
}
int acl_is_user_banned(struct acl_handle* handle, const char* data)
{
if (!handle) return 0;
STR_LIST_CONTAINS(handle->users_banned, data);
}
int acl_is_user_denied(struct acl_handle* handle, const char* data)
{
if (!handle) return 0;
STR_LIST_CONTAINS(handle->users_denied, data);
}
int acl_user_ban_nick(struct acl_handle* handle, const char* nick)
{
struct user_access_info* info = hub_malloc_zero(sizeof(struct user_access_info));
if (!info)
{
hub_log(log_error, "ACL error: Out of memory!");
return -1;
}
list_append(handle->users_banned, hub_strdup(nick));
return 0;
}
int acl_user_ban_cid(struct acl_handle* handle, const char* cid)
{
struct user_access_info* info = hub_malloc_zero(sizeof(struct user_access_info));
if (!info)
{
hub_log(log_error, "ACL error: Out of memory!");
return -1;
}
list_append(handle->cids, hub_strdup(cid));
return 0;
}
int acl_user_unban_nick(struct acl_handle* handle, const char* nick)
{
return -1;
}
int acl_user_unban_cid(struct acl_handle* handle, const char* cid)
{
return -1;
}
int acl_is_ip_banned(struct acl_handle* handle, const char* ip_address)
{
struct ip_addr_encap raw;
struct ip_ban_record* info = (struct ip_ban_record*) list_get_first(handle->networks);
ip_convert_to_binary(ip_address, &raw);
while (info)
{
if (acl_check_ip_range(&raw, info))
{
return 1;
}
info = (struct ip_ban_record*) list_get_next(handle->networks);
}
return 0;
}
int acl_is_ip_nat_override(struct acl_handle* handle, const char* ip_address)
{
struct ip_addr_encap raw;
struct ip_ban_record* info = (struct ip_ban_record*) list_get_first(handle->nat_override);
ip_convert_to_binary(ip_address, &raw);
while (info)
{
if (acl_check_ip_range(&raw, info))
{
return 1;
}
info = (struct ip_ban_record*) list_get_next(handle->nat_override);
}
return 0;
}
int acl_check_ip_range(struct ip_addr_encap* addr, struct ip_ban_record* info)
{
return (addr->af == info->lo.af && ip_compare(&info->lo, addr) <= 0 && ip_compare(addr, &info->hi) <= 0);
}
/*
* This will generate the same challenge to the same user, always.
* The challenge is made up of the time of the user connected
* seconds since the unix epoch (modulus 1 million)
* and the SID of the user (0-1 million).
*/
const char* acl_password_generate_challenge(struct acl_handle* acl, struct user* user)
{
char buf[32];
uint64_t tiger_res[3];
static char tiger_buf[MAX_CID_LEN+1];
snprintf(buf, 32, "%d%d%d", (int) user->net.tm_connected, (int) user->id.sid, (int) user->net.sd);
tiger((uint64_t*) buf, strlen(buf), (uint64_t*) tiger_res);
base32_encode((unsigned char*) tiger_res, TIGERSIZE, tiger_buf);
tiger_buf[MAX_CID_LEN] = 0;
return (const char*) tiger_buf;
}
int acl_password_verify(struct acl_handle* acl, struct user* user, const char* password)
{
char buf[1024];
struct user_access_info* access;
const char* challenge;
char raw_challenge[64];
char password_calc[64];
uint64_t tiger_res[3];
if (!password || !user || strlen(password) != MAX_CID_LEN)
return 0;
access = acl_get_access_info(acl, user->id.nick);
if (!access || !access->password)
return 0;
if (TIGERSIZE+strlen(access->password) >= 1024)
return 0;
challenge = acl_password_generate_challenge(acl, user);
base32_decode(challenge, (unsigned char*) raw_challenge, MAX_CID_LEN);
memcpy(&buf[0], (char*) access->password, strlen(access->password));
memcpy(&buf[strlen(access->password)], raw_challenge, TIGERSIZE);
tiger((uint64_t*) buf, TIGERSIZE+strlen(access->password), (uint64_t*) tiger_res);
base32_encode((unsigned char*) tiger_res, TIGERSIZE, password_calc);
password_calc[MAX_CID_LEN] = 0;
if (strcasecmp(password, password_calc) == 0)
{
return 1;
}
return 0;
}

93
src/core/auth.h Normal file
View File

@@ -0,0 +1,93 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_ACL_H
#define HAVE_UHUB_ACL_H
struct hub_config;
struct user;
struct ip_addr_encap;
enum user_credentials
{
cred_none, /**<<< "User has no credentials (not yet logged in)" */
cred_bot, /**<<< "User is a robot" */
cred_guest, /**<<< "User is a guest (unregistered user)" */
cred_user, /**<<< "User is identified as a registered user" */
cred_operator, /**<<< "User is identified as a hub operator" */
cred_super, /**<<< "User is a super user" (not used) */
cred_admin, /**<<< "User is identified as a hub administrator/owner" */
cred_link, /**<<< "User is a link (not used currently)" */
};
const char* get_user_credential_string(enum user_credentials cred);
struct user_access_info
{
char* username; /* name of user, cid or IP range */
char* password; /* password */
enum user_credentials status;
};
struct ip_ban_record
{
struct ip_addr_encap lo;
struct ip_addr_encap hi;
};
struct acl_handle
{
struct linked_list* users; /* Known users. See enum user_status */
struct linked_list* cids; /* Known CIDs */
struct linked_list* networks; /* IP ranges, used for banning */
struct linked_list* nat_override; /* IPs inside these ranges can provide their false IP. Use with care! */
struct linked_list* users_banned; /* Users permanently banned */
struct linked_list* users_denied; /* bad nickname */
};
extern int acl_initialize(struct hub_config* config, struct acl_handle* handle);
extern int acl_shutdown(struct acl_handle* handle);
extern struct user_access_info* acl_get_access_info(struct acl_handle* handle, const char* name);
extern int acl_is_cid_banned(struct acl_handle* handle, const char* cid);
extern int acl_is_ip_banned(struct acl_handle* handle, const char* ip_address);
extern int acl_is_ip_nat_override(struct acl_handle* handle, const char* ip_address);
extern int acl_is_user_banned(struct acl_handle* handle, const char* name);
extern int acl_is_user_denied(struct acl_handle* handle, const char* name);
extern int acl_user_ban_nick(struct acl_handle* handle, const char* nick);
extern int acl_user_ban_cid(struct acl_handle* handle, const char* cid);
extern int acl_user_unban_nick(struct acl_handle* handle, const char* nick);
extern int acl_user_unban_cid(struct acl_handle* handle, const char* cid);
extern int acl_check_ip_range(struct ip_addr_encap* addr, struct ip_ban_record* info);
extern const char* acl_password_generate_challenge(struct acl_handle* acl, struct user* user);
/**
* Verify a password.
*
* @param password the hashed password (based on the nonce).
* @return 1 if the password matches, or 0 if the password is incorrect.
*/
extern int acl_password_verify(struct acl_handle* acl, struct user* user, const char* password);
#endif /* HAVE_UHUB_ACL_H */

380
src/core/commands.c Normal file
View File

@@ -0,0 +1,380 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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 DEBUG
#define CRASH_DEBUG
#endif
#define MAX_HELP_MSG 1024
struct hub_command
{
const char* message;
char* prefix;
size_t prefix_len;
struct linked_list* args;
};
typedef int (*command_handler)(struct hub_info* hub, struct user* user, struct hub_command*);
struct commands_handler
{
const char* prefix;
size_t length;
const char* args;
enum user_credentials cred;
command_handler handler;
const char* description;
};
static struct commands_handler command_handlers[];
static void command_destroy(struct hub_command* cmd)
{
if (!cmd) return;
hub_free(cmd->prefix);
if (cmd->args)
{
list_clear(cmd->args, &hub_free);
list_destroy(cmd->args);
}
hub_free(cmd);
}
static struct hub_command* command_create(const char* message)
{
struct hub_command* cmd = hub_malloc_zero(sizeof(struct hub_command));
if (!cmd) return 0;
cmd->message = message;
cmd->args = list_create();
int n = split_string(message, "\\s", cmd->args, 0);
if (n <= 0)
{
command_destroy(cmd);
return 0;
}
char* prefix = list_get_first(cmd->args);
if (prefix[0] && prefix[1])
{
cmd->prefix = hub_strdup(&prefix[1]);
cmd->prefix_len = strlen(cmd->prefix);
}
else
{
command_destroy(cmd);
return 0;
}
list_remove(cmd->args, prefix);
hub_free(prefix);
return cmd;
}
static void send_message(struct hub_info* hub, struct user* user, const char* message)
{
char* buffer = adc_msg_escape(message);
struct adc_message* command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6);
adc_msg_add_argument(command, buffer);
route_to_user(hub, user, command);
adc_msg_free(command);
hub_free(buffer);
}
static int command_access_denied(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
char temp[128];
snprintf(temp, 128, "*** %s: Access denied.", cmd->prefix);
send_message(hub, user, temp);
return 0;
}
static int command_not_found(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
char temp[128];
snprintf(temp, 128, "*** %s: Command not found", cmd->prefix);
send_message(hub, user, temp);
return 0;
}
static int command_status_user_not_found(struct hub_info* hub, struct user* user, struct hub_command* cmd, const char* nick)
{
char temp[128];
snprintf(temp, 128, "*** %s: No user \"%s\"", cmd->prefix, nick);
send_message(hub, user, temp);
return 0;
}
const char* command_get_syntax(struct commands_handler* handler)
{
static char args[128];
args[0] = 0;
size_t n = 0;
if (handler->args)
{
for (n = 0; n < strlen(handler->args); n++)
{
if (n > 0) strcat(args, " ");
switch (handler->args[n])
{
case 'n': strcat(args, "<nick>"); break;
case 'c': strcat(args, "<cid>"); break;
case 'a': strcat(args, "<addr>"); break;
}
}
}
return args;
}
static int command_arg_mismatch(struct hub_info* hub, struct user* user, struct hub_command* cmd, struct commands_handler* handler)
{
char temp[256];
const char* args = command_get_syntax(handler);
if (args) snprintf(temp, 256, "*** %s: Use: !%s %s", cmd->prefix, cmd->prefix, args);
else snprintf(temp, 256, "*** %s: Use: !%s", cmd->prefix, cmd->prefix);
send_message(hub, user, temp);
return 0;
}
static int command_status(struct hub_info* hub, struct user* user, struct hub_command* cmd, const char* message)
{
char temp[1024];
snprintf(temp, 1024, "*** %s: %s", cmd->prefix, message);
send_message(hub, user, temp);
return 0;
}
static int command_stats(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
char temp[128];
snprintf(temp, 128, "%zu users, peak: %zu. Network (up/down): %d/%d KB/s, peak: %d/%d KB/s",
hub->users->count,
hub->users->count_peak,
(int) hub->stats.net_tx / 1024,
(int) hub->stats.net_rx / 1024,
(int) hub->stats.net_tx_peak / 1024,
(int) hub->stats.net_rx_peak / 1024);
return command_status(hub, user, cmd, temp);
}
static int command_help(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
size_t n;
char msg[MAX_HELP_MSG];
msg[0] = 0;
strcat(msg, "Available commands:\n");
for (n = 0; command_handlers[n].prefix; n++)
{
if (command_handlers[n].cred <= user->credentials)
{
strcat(msg, "!");
strcat(msg, command_handlers[n].prefix);
strcat(msg, " - ");
strcat(msg, command_handlers[n].description);
strcat(msg, "\n");
}
}
return command_status(hub, user, cmd, msg);
}
static int command_uptime(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
char tmp[128];
size_t d;
size_t h;
size_t m;
size_t D = (size_t) difftime(time(0), hub->tm_started);
d = D / (24 * 3600);
D = D % (24 * 3600);
h = D / 3600;
D = D % 3600;
m = D / 60;
tmp[0] = 0;
if (d)
{
strcat(tmp, uhub_itoa((int) d));
strcat(tmp, " day");
if (d != 1) strcat(tmp, "s");
strcat(tmp, ", ");
}
if (h < 10) strcat(tmp, "0");
strcat(tmp, uhub_itoa((int) h));
strcat(tmp, ":");
if (m < 10) strcat(tmp, "0");
strcat(tmp, uhub_itoa((int) m));
return command_status(hub, user, cmd, tmp);
}
static int command_kick(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
char* nick = list_get_first(cmd->args);
struct user* target = uman_get_user_by_nick(hub, nick);
if (!target)
return command_status_user_not_found(hub, user, cmd, nick);
if (target == user)
return command_status(hub, user, cmd, "Cannot kick yourself");
hub_disconnect_user(hub, target, quit_kicked);
return command_status(hub, user, cmd, nick);
}
static int command_ban(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
char* nick = list_get_first(cmd->args);
struct user* target = uman_get_user_by_nick(hub, nick);
if (!target)
return command_status_user_not_found(hub, user, cmd, nick);
if (target == user)
return command_status(hub, user, cmd, "Cannot kick/ban yourself");
hub_disconnect_user(hub, target, quit_kicked);
acl_user_ban_nick(hub->acl, target->id.nick);
acl_user_ban_cid(hub->acl, target->id.cid);
return command_status(hub, user, cmd, nick);
}
static int command_unban(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
return command_status(hub, user, cmd, "Not implemented");
}
static int command_reload(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
hub->status = hub_status_restart;
return command_status(hub, user, cmd, "Reloading configuration...");
}
static int command_shutdown(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
hub->status = hub_status_shutdown;
return command_status(hub, user, cmd, "Hub shutting down...");
}
static int command_version(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
return command_status(hub, user, cmd, "Powered by " PRODUCT "/" VERSION);
}
static int command_myip(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
char tmp[128];
snprintf(tmp, 128, "Your address is \"%s\"", ip_convert_to_string(&user->net.ipaddr));
return command_status(hub, user, cmd, tmp);
}
static int command_getip(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
char tmp[128];
char* nick = list_get_first(cmd->args);
struct user* target = uman_get_user_by_nick(hub, nick);
if (!target)
return command_status_user_not_found(hub, user, cmd, nick);
snprintf(tmp, 128, "%s has address \"%s\"", nick, ip_convert_to_string(&target->net.ipaddr));
return command_status(hub, user, cmd, tmp);
}
#ifdef CRASH_DEBUG
static int command_crash(struct hub_info* hub, struct user* user, struct hub_command* cmd)
{
void (*crash)(void) = NULL;
crash();
return 0;
}
#endif
int command_dipatcher(struct hub_info* hub, struct user* user, const char* message)
{
size_t n = 0;
int rc;
/* Parse and validate the command */
struct hub_command* cmd = command_create(message);
if (!cmd) return 1;
for (n = 0; command_handlers[n].prefix; n++)
{
struct commands_handler* handler = &command_handlers[n];
if (cmd->prefix_len != handler->length)
continue;
if (!strncmp(cmd->prefix, handler->prefix, handler->length))
{
if (handler->cred <= user->credentials)
{
if (!handler->args || (handler->args && list_size(cmd->args) >= strlen(handler->args)))
{
rc = handler->handler(hub, user, cmd);
}
else
{
rc = command_arg_mismatch(hub, user, cmd, handler);
}
command_destroy(cmd);
return rc;
}
else
{
rc = command_access_denied(hub, user, cmd);
command_destroy(cmd);
return rc;
}
}
}
command_not_found(hub, user, cmd);
command_destroy(cmd);
return 1;
}
static struct commands_handler command_handlers[] = {
{ "help", 4, 0, cred_guest, command_help, "Show this help message." },
{ "stats", 5, 0, cred_super, command_stats, "Show hub statistics." },
{ "version", 7, 0, cred_guest, command_version, "Show hub version info." },
{ "uptime", 6, 0, cred_guest, command_uptime, "Display hub uptime info." },
{ "kick", 4, "n", cred_operator, command_kick, "Kick a user" },
{ "ban", 3, "n", cred_operator, command_ban, "Ban a user" },
{ "unban", 5, "n", cred_operator, command_unban, "Lift ban on a user" },
{ "reload", 6, 0, cred_admin, command_reload, "Reload configuration files." },
{ "shutdown", 8, 0, cred_admin, command_shutdown, "Shutdown hub." },
{ "myip", 4, 0, cred_guest, command_myip, "Show your own IP." },
{ "getip", 5, "n", cred_operator, command_getip, "Show IP address for a user" },
#ifdef CRASH_DEBUG
{ "crash", 5, 0, cred_admin, command_crash, "Crash the hub (DEBUG)." },
#endif
{ 0, 0, 0, cred_none, command_help, "" }
};

35
src/core/commands.h Normal file
View File

@@ -0,0 +1,35 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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"
#define CHAT_MSG_HANDLED 1
#define CHAT_MSG_IGNORED 0
#define CHAT_MSG_INVALID -1
typedef int (*plugin_event_chat_message)(struct hub_info*, struct user*, struct adc_message*);
struct command_info
{
const char* prefix;
enum user_credentials cred;
plugin_event_chat_message function;
};
int command_dipatcher(struct hub_info* hub, struct user* user, const char* message);

524
src/core/config.c Normal file
View File

@@ -0,0 +1,524 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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"
#ifndef INT_MAX
#define INT_MAX 0x7fffffff
#endif
#ifndef INT_MIN
#define INT_MIN (-0x7fffffff - 1)
#endif
#define CFG_APPLY_BOOLEAN(KEY, TARGET) \
if (strcmp(KEY, key) == 0) \
{ \
if (strlen(data) == 1 && (data[0] == '1')) TARGET = 1; \
else if (strlen(data) == 1 && (data[0] == '0')) TARGET = 0; \
else if (strncasecmp(data, "true", 4) == 0) TARGET = 1; \
else if (strncasecmp(data, "false", 5) == 0) TARGET = 0; \
else if (strncasecmp(data, "yes", 3) == 0) TARGET = 1; \
else if (strncasecmp(data, "no", 2) == 0) TARGET = 0; \
else if (strncasecmp(data, "on", 2) == 0) TARGET = 1; \
else if (strncasecmp(data, "off", 3) == 0) TARGET = 0; \
else\
{ \
hub_log(log_fatal, "Configuration error on line %d: '%s' must be either '1' or '0'", line_count, key); \
return -1; \
} \
TARGET |= 0x80000000; \
return 0; \
}
#define CFG_APPLY_STRING(KEY, TARGET) \
if (strcmp(KEY, key) == 0) \
{ \
TARGET = hub_strdup(data); \
return 0; \
}
#define CFG_APPLY_INTEGER(KEY, TARGET) \
if (strcmp(KEY, key) == 0) \
{ \
char* endptr; \
int val; \
errno = 0; \
val = strtol(data, &endptr, 10); \
if (((errno == ERANGE && (val == INT_MAX || val == INT_MIN)) || (errno != 0 && val == 0)) || endptr == data) { \
hub_log(log_fatal, "Configuration error on line %d: '%s' must be a number", line_count, key); \
return -1; \
} \
TARGET = val; \
return 0; \
}
#define DEFAULT_STRING(KEY, VALUE) \
{ \
if (config->KEY == 0) \
config->KEY = hub_strdup(VALUE); \
}
#define DEFAULT_INTEGER(KEY, VALUE) \
{ \
if (config->KEY == 0) \
config->KEY = VALUE; \
}
#define DEFAULT_BOOLEAN(KEY, VALUE) \
{ \
if (config->KEY & 0x80000000) \
{ \
config->KEY = config->KEY & 0x000000ff; \
} \
else \
{ \
config->KEY = VALUE; \
} \
}
#define GET_STR(NAME) CFG_APPLY_STRING ( #NAME , config->NAME )
#define GET_INT(NAME) CFG_APPLY_INTEGER( #NAME , config->NAME )
#define GET_BOOL(NAME) CFG_APPLY_BOOLEAN( #NAME , config->NAME )
#define IGNORED(NAME) \
if (strcmp(#NAME, key) == 0) \
{ \
hub_log(log_warning, "Configuration option %s deprecated and ingnored.", key); \
return 0; \
} \
/* default configuration values */
#define DEF_SERVER_BIND_ADDR "any"
#define DEF_SERVER_PORT 1511
#define DEF_HUB_NAME "uhub"
#define DEF_HUB_DESCRIPTION ""
#define DEF_HUB_ENABLED 1
#define DEF_FILE_ACL ""
#define DEF_FILE_MOTD ""
#define DEF_MAX_USERS 500
#define DEF_MAX_RECV_BUFFER 4096
#define DEF_MAX_SEND_BUFFER 131072
#define DEF_MAX_SEND_BUFFER_SOFT 98304
#define DEF_SHOW_BANNER 1
#define DEF_REGISTERED_USERS_ONLY 0
#define DEF_CHAT_ONLY 0
#define DEF_CHAT_IS_PRIVILEGED 0
#define DEF_LOW_BANDWIDTH_MODE 0
#define DEF_LIMIT_MAX_HUBS_USER 0
#define DEF_LIMIT_MAX_HUBS_REG 0
#define DEF_LIMIT_MAX_HUBS_OP 0
#define DEF_LIMIT_MAX_HUBS 0
#define DEF_LIMIT_MIN_HUBS_USER 0
#define DEF_LIMIT_MIN_HUBS_REG 0
#define DEF_LIMIT_MIN_HUBS_OP 0
#define DEF_LIMIT_MIN_SHARE 0
#define DEF_LIMIT_MAX_SHARE 0
#define DEF_LIMIT_MIN_SLOTS 0
#define DEF_LIMIT_MAX_SLOTS 0
#define DEF_MSG_HUB_FULL "Hub is full"
#define DEF_MSG_HUB_DISABLED "Hub is disabled"
#define DEF_MSG_HUB_REGISTERED_USERS_ONLY "Hub is for registered users only"
#define DEF_MSG_INF_ERROR_NICK_MISSING "No nickname given"
#define DEF_MSG_INF_ERROR_NICK_MULTIPLE "Multiple nicknames given"
#define DEF_MSG_INF_ERROR_NICK_INVALID "Nickname is invalid"
#define DEF_MSG_INF_ERROR_NICK_LONG "Nickname too long"
#define DEF_MSG_INF_ERROR_NICK_SHORT "Nickname too short"
#define DEF_MSG_INF_ERROR_NICK_SPACES "Nickname cannot start with spaces"
#define DEF_MSG_INF_ERROR_NICK_BAD_CHARS "Nickname contains invalid characters"
#define DEF_MSG_INF_ERROR_NICK_NOT_UTF8 "Nickname is not valid utf8"
#define DEF_MSG_INF_ERROR_NICK_TAKEN "Nickname is already in use"
#define DEF_MSG_INF_ERROR_NICK_RESTRICTED "Nickname cannot be used on this hub"
#define DEF_MSG_INF_ERROR_CID_INVALID "CID is not valid"
#define DEF_MSG_INF_ERROR_CID_MISSING "CID is not specified"
#define DEF_MSG_INF_ERROR_CID_TAKEN "CID is taken"
#define DEF_MSG_INF_ERROR_PID_MISSING "PID is not specified"
#define DEF_MSG_INF_ERROR_PID_INVALID "PID is invalid"
#define DEF_MSG_BAN_PERMANENTLY "Banned permanently"
#define DEF_MSG_BAN_TEMPORARILY "Banned temporarily"
#define DEF_MSG_AUTH_INVALID_PASSWORD "Password is wrong"
#define DEF_MSG_AUTH_USER_NOT_FOUND "User not found in password database"
#define DEF_MSG_ERROR_NO_MEMORY "No memory"
#define DEF_MSG_USER_SHARE_SIZE_LOW "User is not sharing enough"
#define DEF_MSG_USER_SHARE_SIZE_HIGH "User is sharing too much"
#define DEF_MSG_USER_SLOTS_LOW "User have too few upload slots."
#define DEF_MSG_USER_SLOTS_HIGH "User have too many upload slots."
#define DEF_MSG_USER_HUB_LIMIT_LOW "User is on too few hubs."
#define DEF_MSG_USER_HUB_LIMIT_HIGH "User is on too many hubs."
void config_defaults(struct hub_config* config)
{
DEFAULT_STRING (server_bind_addr, DEF_SERVER_BIND_ADDR);
DEFAULT_STRING (hub_name, DEF_HUB_NAME);
DEFAULT_STRING (hub_description, DEF_HUB_DESCRIPTION);
DEFAULT_BOOLEAN(hub_enabled, DEF_HUB_ENABLED);
DEFAULT_STRING (file_acl, DEF_FILE_ACL);
DEFAULT_STRING (file_motd, DEF_FILE_MOTD);
DEFAULT_INTEGER(server_port, DEF_SERVER_PORT);
DEFAULT_INTEGER(max_users, DEF_MAX_USERS);
DEFAULT_INTEGER(max_recv_buffer, DEF_MAX_RECV_BUFFER);
DEFAULT_INTEGER(max_send_buffer, DEF_MAX_SEND_BUFFER);
DEFAULT_INTEGER(max_send_buffer_soft, DEF_MAX_SEND_BUFFER_SOFT);
DEFAULT_BOOLEAN(show_banner, DEF_SHOW_BANNER);
DEFAULT_BOOLEAN(chat_only, DEF_CHAT_ONLY);
DEFAULT_BOOLEAN(chat_is_privileged, DEF_CHAT_IS_PRIVILEGED);
DEFAULT_BOOLEAN(low_bandwidth_mode, DEF_LOW_BANDWIDTH_MODE);
DEFAULT_BOOLEAN(registered_users_only, DEF_REGISTERED_USERS_ONLY);
/* Limits enforced on users */
DEFAULT_INTEGER(limit_max_hubs_user, DEF_LIMIT_MAX_HUBS_USER);
DEFAULT_INTEGER(limit_max_hubs_reg, DEF_LIMIT_MAX_HUBS_REG);
DEFAULT_INTEGER(limit_max_hubs_op, DEF_LIMIT_MAX_HUBS_OP);
DEFAULT_INTEGER(limit_min_hubs_user, DEF_LIMIT_MIN_HUBS_USER);
DEFAULT_INTEGER(limit_min_hubs_reg, DEF_LIMIT_MIN_HUBS_REG);
DEFAULT_INTEGER(limit_min_hubs_op, DEF_LIMIT_MIN_HUBS_OP);
DEFAULT_INTEGER(limit_max_hubs, DEF_LIMIT_MAX_HUBS);
DEFAULT_INTEGER(limit_min_share, DEF_LIMIT_MIN_SHARE);
DEFAULT_INTEGER(limit_max_share, DEF_LIMIT_MAX_SHARE);
DEFAULT_INTEGER(limit_min_slots, DEF_LIMIT_MIN_SLOTS);
DEFAULT_INTEGER(limit_max_slots, DEF_LIMIT_MAX_SLOTS);
/* Status/error strings */
DEFAULT_STRING (msg_hub_full, DEF_MSG_HUB_FULL);
DEFAULT_STRING (msg_hub_disabled, DEF_MSG_HUB_DISABLED)
DEFAULT_STRING (msg_hub_registered_users_only, DEF_MSG_HUB_REGISTERED_USERS_ONLY);
DEFAULT_STRING (msg_inf_error_nick_missing, DEF_MSG_INF_ERROR_NICK_MISSING);
DEFAULT_STRING (msg_inf_error_nick_multiple, DEF_MSG_INF_ERROR_NICK_MULTIPLE);
DEFAULT_STRING (msg_inf_error_nick_invalid, DEF_MSG_INF_ERROR_NICK_INVALID);
DEFAULT_STRING (msg_inf_error_nick_long, DEF_MSG_INF_ERROR_NICK_LONG);
DEFAULT_STRING (msg_inf_error_nick_short, DEF_MSG_INF_ERROR_NICK_SHORT);
DEFAULT_STRING (msg_inf_error_nick_spaces, DEF_MSG_INF_ERROR_NICK_SPACES);
DEFAULT_STRING (msg_inf_error_nick_bad_chars, DEF_MSG_INF_ERROR_NICK_BAD_CHARS);
DEFAULT_STRING (msg_inf_error_nick_not_utf8, DEF_MSG_INF_ERROR_NICK_NOT_UTF8);
DEFAULT_STRING (msg_inf_error_nick_taken, DEF_MSG_INF_ERROR_NICK_TAKEN);
DEFAULT_STRING (msg_inf_error_nick_restricted, DEF_MSG_INF_ERROR_NICK_RESTRICTED);
DEFAULT_STRING (msg_inf_error_cid_invalid, DEF_MSG_INF_ERROR_CID_INVALID);
DEFAULT_STRING (msg_inf_error_cid_missing, DEF_MSG_INF_ERROR_CID_MISSING);
DEFAULT_STRING (msg_inf_error_cid_taken, DEF_MSG_INF_ERROR_CID_TAKEN);
DEFAULT_STRING (msg_inf_error_pid_missing, DEF_MSG_INF_ERROR_PID_MISSING);
DEFAULT_STRING (msg_inf_error_pid_invalid, DEF_MSG_INF_ERROR_PID_INVALID);
DEFAULT_STRING (msg_ban_permanently, DEF_MSG_BAN_PERMANENTLY);
DEFAULT_STRING (msg_ban_temporarily, DEF_MSG_BAN_TEMPORARILY);
DEFAULT_STRING (msg_auth_invalid_password, DEF_MSG_AUTH_INVALID_PASSWORD);
DEFAULT_STRING (msg_auth_user_not_found, DEF_MSG_AUTH_USER_NOT_FOUND);
DEFAULT_STRING (msg_error_no_memory, DEF_MSG_ERROR_NO_MEMORY);
DEFAULT_STRING (msg_user_share_size_low, DEF_MSG_USER_SHARE_SIZE_LOW);
DEFAULT_STRING (msg_user_share_size_high, DEF_MSG_USER_SHARE_SIZE_HIGH);
DEFAULT_STRING (msg_user_slots_low, DEF_MSG_USER_SLOTS_LOW);
DEFAULT_STRING (msg_user_slots_high, DEF_MSG_USER_SLOTS_HIGH);
DEFAULT_STRING (msg_user_hub_limit_low, DEF_MSG_USER_HUB_LIMIT_LOW);
DEFAULT_STRING (msg_user_hub_limit_high, DEF_MSG_USER_HUB_LIMIT_HIGH);
DEFAULT_INTEGER(tls_enable, 0);
DEFAULT_INTEGER(tls_require, 0);
DEFAULT_STRING (tls_certificate, "");
DEFAULT_STRING (tls_private_key, "");
}
static int apply_config(struct hub_config* config, char* key, char* data, int line_count)
{
GET_STR (file_acl);
GET_STR (file_motd);
GET_STR (server_bind_addr);
GET_INT (server_port);
GET_STR (hub_name);
GET_STR (hub_description);
GET_BOOL(hub_enabled);
GET_INT (max_users);
GET_INT (max_recv_buffer);
GET_INT (max_send_buffer);
GET_INT (max_send_buffer_soft);
GET_BOOL(show_banner);
GET_BOOL(chat_only);
GET_BOOL(chat_is_privileged);
GET_BOOL(low_bandwidth_mode);
GET_BOOL(registered_users_only);
/* Limits enforced on users */
GET_INT(limit_max_hubs_user);
GET_INT(limit_max_hubs_reg);
GET_INT(limit_max_hubs_op);
GET_INT(limit_min_hubs_user);
GET_INT(limit_min_hubs_reg);
GET_INT(limit_min_hubs_op);
GET_INT(limit_max_hubs);
GET_INT(limit_min_share);
GET_INT(limit_max_share);
GET_INT(limit_min_slots);
GET_INT(limit_max_slots);
/* Status/error strings */
GET_STR (msg_hub_full);
GET_STR (msg_hub_disabled);
GET_STR (msg_hub_registered_users_only);
GET_STR (msg_inf_error_nick_missing);
GET_STR (msg_inf_error_nick_multiple);
GET_STR (msg_inf_error_nick_invalid);
GET_STR (msg_inf_error_nick_long);
GET_STR (msg_inf_error_nick_short);
GET_STR (msg_inf_error_nick_spaces);
GET_STR (msg_inf_error_nick_bad_chars);
GET_STR (msg_inf_error_nick_not_utf8);
GET_STR (msg_inf_error_nick_taken);
GET_STR (msg_inf_error_nick_restricted);
GET_STR (msg_inf_error_cid_invalid);
GET_STR (msg_inf_error_cid_missing);
GET_STR (msg_inf_error_cid_taken);
GET_STR (msg_inf_error_pid_missing);
GET_STR (msg_inf_error_pid_invalid);
GET_STR (msg_ban_permanently);
GET_STR (msg_ban_temporarily);
GET_STR (msg_auth_invalid_password);
GET_STR (msg_auth_user_not_found);
GET_STR (msg_error_no_memory);
GET_STR (msg_user_share_size_low);
GET_STR (msg_user_share_size_high);
GET_STR (msg_user_slots_low);
GET_STR (msg_user_slots_high);
GET_STR (msg_user_hub_limit_low);
GET_STR (msg_user_hub_limit_high);
GET_BOOL(tls_enable);
GET_BOOL(tls_require);
GET_STR (tls_certificate);
GET_STR (tls_private_key);
/* Still here -- unknown directive */
hub_log(log_fatal, "Unknown configuration directive: '%s'", key);
return -1;
}
void free_config(struct hub_config* config)
{
hub_free(config->server_bind_addr);
hub_free(config->file_motd);
hub_free(config->file_acl);
hub_free(config->hub_name);
hub_free(config->hub_description);
hub_free(config->msg_hub_full);
hub_free(config->msg_hub_disabled);
hub_free(config->msg_hub_registered_users_only);
hub_free(config->msg_inf_error_nick_missing);
hub_free(config->msg_inf_error_nick_multiple);
hub_free(config->msg_inf_error_nick_invalid);
hub_free(config->msg_inf_error_nick_long);
hub_free(config->msg_inf_error_nick_short);
hub_free(config->msg_inf_error_nick_spaces);
hub_free(config->msg_inf_error_nick_bad_chars);
hub_free(config->msg_inf_error_nick_not_utf8);
hub_free(config->msg_inf_error_nick_taken);
hub_free(config->msg_inf_error_nick_restricted);
hub_free(config->msg_inf_error_cid_invalid);
hub_free(config->msg_inf_error_cid_missing);
hub_free(config->msg_inf_error_cid_taken);
hub_free(config->msg_inf_error_pid_missing);
hub_free(config->msg_inf_error_pid_invalid);
hub_free(config->msg_ban_permanently);
hub_free(config->msg_ban_temporarily);
hub_free(config->msg_auth_invalid_password);
hub_free(config->msg_auth_user_not_found);
hub_free(config->msg_error_no_memory);
hub_free(config->msg_user_share_size_low);
hub_free(config->msg_user_share_size_high);
hub_free(config->msg_user_slots_low);
hub_free(config->msg_user_slots_high);
hub_free(config->msg_user_hub_limit_low);
hub_free(config->msg_user_hub_limit_high);
hub_free(config->tls_certificate);
hub_free(config->tls_private_key);
memset(config, 0, sizeof(struct hub_config));
}
#define DUMP_STR(NAME, DEFAULT) \
if (ignore_defaults) \
{ \
if (strcmp(config->NAME, DEFAULT) != 0) \
fprintf(stdout, "%s = \"%s\"\n", #NAME , config->NAME); \
} \
else \
fprintf(stdout, "%s = \"%s\"\n", #NAME , config->NAME); \
#define DUMP_INT(NAME, DEFAULT) \
if (ignore_defaults) \
{ \
if (config->NAME != DEFAULT) \
fprintf(stdout, "%s = %d\n", #NAME , config->NAME); \
} \
else \
fprintf(stdout, "%s = %d\n", #NAME , config->NAME); \
#define DUMP_BOOL(NAME, DEFAULT) \
if (ignore_defaults) \
{ \
if (config->NAME != DEFAULT) \
fprintf(stdout, "%s = %s\n", #NAME , (config->NAME ? "yes" : "no")); \
} \
else \
fprintf(stdout, "%s = %s\n", #NAME , (config->NAME ? "yes" : "no"));
void dump_config(struct hub_config* config, int ignore_defaults)
{
DUMP_STR (file_acl, DEF_FILE_ACL);
DUMP_STR (file_motd, DEF_FILE_MOTD);
DUMP_STR (server_bind_addr, DEF_SERVER_BIND_ADDR);
DUMP_INT (server_port, DEF_SERVER_PORT);
DUMP_STR (hub_name, DEF_HUB_NAME);
DUMP_STR (hub_description, DEF_HUB_DESCRIPTION);
DUMP_BOOL(hub_enabled, DEF_HUB_ENABLED);
DUMP_INT (max_users, DEF_MAX_USERS);
DUMP_INT (max_recv_buffer, DEF_MAX_RECV_BUFFER);
DUMP_INT (max_send_buffer, DEF_MAX_SEND_BUFFER);
DUMP_INT (max_send_buffer_soft, DEF_MAX_SEND_BUFFER_SOFT);
DUMP_BOOL(show_banner, DEF_SHOW_BANNER);
DUMP_BOOL(chat_only, DEF_CHAT_ONLY);
DUMP_BOOL(chat_is_privileged, DEF_CHAT_IS_PRIVILEGED);
DUMP_BOOL(low_bandwidth_mode, DEF_LOW_BANDWIDTH_MODE);
DUMP_BOOL(registered_users_only, DEF_REGISTERED_USERS_ONLY);
/* Limits enforced on users */
DUMP_INT(limit_max_hubs_user, DEF_LIMIT_MAX_HUBS_USER);
DUMP_INT(limit_max_hubs_reg, DEF_LIMIT_MAX_HUBS_REG);
DUMP_INT(limit_max_hubs_op, DEF_LIMIT_MAX_HUBS_OP);
DUMP_INT(limit_min_hubs_user, DEF_LIMIT_MIN_HUBS_USER);
DUMP_INT(limit_min_hubs_reg, DEF_LIMIT_MIN_HUBS_REG);
DUMP_INT(limit_min_hubs_op, DEF_LIMIT_MIN_HUBS_OP);
DUMP_INT(limit_max_hubs, DEF_LIMIT_MAX_HUBS);
DUMP_INT(limit_min_share, DEF_LIMIT_MIN_SHARE);
DUMP_INT(limit_max_share, DEF_LIMIT_MAX_SHARE);
DUMP_INT(limit_min_slots, DEF_LIMIT_MIN_SLOTS);
DUMP_INT(limit_max_slots, DEF_LIMIT_MAX_SLOTS);
/* Status/error strings */
DUMP_STR (msg_hub_full, DEF_MSG_HUB_FULL);
DUMP_STR (msg_hub_disabled, DEF_MSG_HUB_DISABLED);
DUMP_STR (msg_hub_registered_users_only, DEF_MSG_HUB_REGISTERED_USERS_ONLY);
DUMP_STR (msg_inf_error_nick_missing, DEF_MSG_INF_ERROR_NICK_MISSING);
DUMP_STR (msg_inf_error_nick_multiple, DEF_MSG_INF_ERROR_NICK_MULTIPLE);
DUMP_STR (msg_inf_error_nick_invalid, DEF_MSG_INF_ERROR_NICK_INVALID);
DUMP_STR (msg_inf_error_nick_long, DEF_MSG_INF_ERROR_NICK_LONG);
DUMP_STR (msg_inf_error_nick_short, DEF_MSG_INF_ERROR_NICK_SHORT);
DUMP_STR (msg_inf_error_nick_spaces, DEF_MSG_INF_ERROR_NICK_SPACES);
DUMP_STR (msg_inf_error_nick_bad_chars, DEF_MSG_INF_ERROR_NICK_BAD_CHARS);
DUMP_STR (msg_inf_error_nick_not_utf8, DEF_MSG_INF_ERROR_NICK_NOT_UTF8);
DUMP_STR (msg_inf_error_nick_taken, DEF_MSG_INF_ERROR_NICK_TAKEN);
DUMP_STR (msg_inf_error_nick_restricted, DEF_MSG_INF_ERROR_NICK_RESTRICTED);
DUMP_STR (msg_inf_error_cid_invalid, DEF_MSG_INF_ERROR_CID_INVALID);
DUMP_STR (msg_inf_error_cid_missing, DEF_MSG_INF_ERROR_CID_MISSING);
DUMP_STR (msg_inf_error_cid_taken, DEF_MSG_INF_ERROR_CID_TAKEN);
DUMP_STR (msg_inf_error_pid_missing, DEF_MSG_INF_ERROR_PID_MISSING);
DUMP_STR (msg_inf_error_pid_invalid, DEF_MSG_INF_ERROR_PID_INVALID);
DUMP_STR (msg_ban_permanently, DEF_MSG_BAN_PERMANENTLY);
DUMP_STR (msg_ban_temporarily, DEF_MSG_BAN_TEMPORARILY);
DUMP_STR (msg_auth_invalid_password, DEF_MSG_AUTH_INVALID_PASSWORD);
DUMP_STR (msg_auth_user_not_found, DEF_MSG_AUTH_USER_NOT_FOUND);
DUMP_STR (msg_error_no_memory, DEF_MSG_ERROR_NO_MEMORY);
DUMP_STR (msg_user_share_size_low, DEF_MSG_USER_SHARE_SIZE_LOW);
DUMP_STR (msg_user_share_size_high, DEF_MSG_USER_SHARE_SIZE_HIGH);
DUMP_STR (msg_user_slots_low, DEF_MSG_USER_SLOTS_LOW);
DUMP_STR (msg_user_slots_high, DEF_MSG_USER_SLOTS_HIGH);
DUMP_STR (msg_user_hub_limit_low, DEF_MSG_USER_HUB_LIMIT_LOW);
DUMP_STR (msg_user_hub_limit_high, DEF_MSG_USER_HUB_LIMIT_HIGH);
}
static int config_parse_line(char* line, int line_count, void* ptr_data)
{
char* pos;
char* key;
char* data;
struct hub_config* config = (struct hub_config*) ptr_data;
if ((pos = strchr(line, '#')) != NULL)
{
pos[0] = 0;
}
if (!*line) return 0;
#ifdef CONFIG_DUMP
hub_log(log_trace, "config_parse_line(): '%s'", line);
#endif
if (!is_valid_utf8(line))
{
hub_log(log_warning, "Invalid utf-8 characters on line %d", line_count);
}
if ((pos = strchr(line, '=')) != NULL)
{
pos[0] = 0;
}
else
{
return 0;
}
key = line;
data = &pos[1];
key = strip_white_space(key);
data = strip_white_space(data);
if (!*key || !*data)
{
hub_log(log_fatal, "Configuration parse error on line %d", line_count);
return -1;
}
#ifdef CONFIG_DUMP
hub_log(log_trace, "config_parse_line: '%s' => '%s'", key, data);
#endif
return apply_config(config, key, data, line_count);
}
int read_config(const char* file, struct hub_config* config, int allow_missing)
{
int ret;
memset(config, 0, sizeof(struct hub_config));
ret = file_read_lines(file, config, &config_parse_line);
if (ret < 0)
{
if (allow_missing && ret == -2)
{
hub_log(log_debug, "Using default configuration.");
}
else
{
return -1;
}
}
config_defaults(config);
return 0;
}

122
src/core/config.h Normal file
View File

@@ -0,0 +1,122 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_CONFIG_H
#define HAVE_UHUB_CONFIG_H
struct hub_config
{
int server_port; /**<<< "Server port to bind to (default: 1511)" */
char* server_bind_addr; /**<<< "Server bind address (default: '0.0.0.0' or '::')" */
int hub_enabled; /**<<< "Is server enabled (default: 1)" */
int show_banner; /**<<< "Show banner on connect (default: 1)" */
int max_users; /**<<< "Maximum number of users allowed on the hub (default: 500)" */
int registered_users_only; /**<<< "Allow registered users only (default: 0)" */
int chat_only; /**<<< "Allow chat only operation on hub (default: 0)" */
int chat_is_privileged; /**<<< "Allow chat for operators and above only (default: 0) */
char* file_motd; /**<<< "File containing the 'message of the day' (default: '' - no motd)" */
char* file_acl; /**<<< "File containing user database (default: '' - no known users)" */
char* hub_name; /**<<< "Name of hub (default: 'My uhub hub')" */
char* hub_description; /**<<< "Name of hub (default: 'no description')" */
int max_recv_buffer; /**<<< "Max read buffer before parse, per user (default: 4096)" */
int max_send_buffer; /**<<< "Max send buffer before disconnect, per user (default: 128K)" */
int max_send_buffer_soft; /**<<< "Max send buffer before message drops, per user (default: 96K)" */
int low_bandwidth_mode; /**<<< "If this is enabled, the hub will strip off elements from each user's info message to reduce bandwidth usage" */
/* Limits enforced on users */
int limit_max_hubs_user; /**<<< "Max concurrent hubs as a user. (0=off, default: 10)" */
int limit_max_hubs_reg; /**<<< "Max concurrent hubs as registered user. (0=off, default: 10)" */
int limit_max_hubs_op; /**<<< "Max concurrent hubs as operator. (0=off, default: 10)" */
int limit_min_hubs_user; /**<<< "Min concurrent hubs as a user. (0=off, default: 0)" */
int limit_min_hubs_reg; /**<<< "Min concurrent hubs as registered user. (0=off, default: 0)" */
int limit_min_hubs_op; /**<<< "Min concurrent hubs as operator. (0=off, default: 0)" */
int limit_max_hubs; /**<<< "Max total hub connections allowed, user/reg/op combined. (0=off, default: 25)" */
int limit_min_share; /**<<< "Limit minimum share size in megabytes (MiB) (0=off, default: 0)" */
int limit_max_share; /**<<< "Limit maximum share size in megabytes (MiB) (0=off, default: 0)" */
int limit_min_slots; /**<<< "Limit minimum number of slots open per user (0=off, default: 0)" */
int limit_max_slots; /**<<< "Limit maximum number of slots open per user (0=off, default: 0)" */
/* Messages that can be sent to a user */
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" */
char* msg_inf_error_nick_missing; /**<<< "no nickname given" */
char* msg_inf_error_nick_multiple; /**<<< "multiple nicknames given" */
char* msg_inf_error_nick_invalid; /**<<< "generic/unkown" */
char* msg_inf_error_nick_long; /**<<< "nickname too long" */
char* msg_inf_error_nick_short; /**<<< "nickname too short" */
char* msg_inf_error_nick_spaces; /**<<< "nickname cannot start with spaces" */
char* msg_inf_error_nick_bad_chars; /**<<< "nickname contains chars below ascii 32" */
char* msg_inf_error_nick_not_utf8; /**<<< "nickname is not valid utf8" */
char* msg_inf_error_nick_taken; /**<<< "nickname is in use" */
char* msg_inf_error_nick_restricted; /**<<< "nickname cannot be used on this hub" */
char* msg_inf_error_cid_invalid; /**<<< "CID is not valid" */
char* msg_inf_error_cid_missing; /**<<< "CID is not specified" */
char* msg_inf_error_cid_taken; /**<<< "CID is taken" */
char* msg_inf_error_pid_missing; /**<<< "PID is not specified" */
char* msg_inf_error_pid_invalid; /**<<< "PID is invalid" */
char* msg_ban_permanently; /**<<< "Banned permanently" */
char* msg_ban_temporarily; /**<<< "Banned temporarily" */
char* msg_auth_invalid_password; /**<<< "Password is wrong" */
char* msg_auth_user_not_found; /**<<< "User not found in password database" */
char* msg_error_no_memory; /**<<< "No memory" */
char* msg_user_share_size_low; /**<<< "User is not sharing enough" */
char* msg_user_share_size_high; /**<<< "User is sharing too much" */
char* msg_user_slots_low; /**<<< "User have too few upload slots." */
char* msg_user_slots_high; /**<<< "User have too many upload slots." */
char* msg_user_hub_limit_low; /**<<< "User is on too few hubs." */
char* msg_user_hub_limit_high; /**<<< "User is on too many hubs." */
int tls_enable; /**<<< "Enable SSL/TLS support (default: 0)" */
int tls_require; /**<<< "If SSL/TLS enabled, should it be required (default: 0) */
char* tls_certificate; /**<<< "Certificate file (PEM)" */
char* tls_private_key; /**<<< "Private key" */
};
/**
* This initializes the configuration variables, and sets the default
* variables.
*
* NOTE: Any variable is set to it's default variable if zero.
* This function is automatically called in read_config to set any
* configuration that was missing there.
*/
extern void config_defaults(struct hub_config* config);
/**
* Read configuration from file, and use the default variables for
* the missing variables.
*
* @return -1 on error, 0 on success.
*/
extern int read_config(const char* file, struct hub_config* config, int allow_missing);
/**
* Free the configuration data (allocated by read_config, or config_defaults).
*/
extern void free_config(struct hub_config* config);
/**
* Print all configuration data to standard out.
*/
extern void dump_config(struct hub_config* config, int ignore_defaults);
#endif /* HAVE_UHUB_CONFIG_H */

37
src/core/eventid.h Normal file
View File

@@ -0,0 +1,37 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_EVENT_ID_H
#define HAVE_UHUB_EVENT_ID_H
/* User join or quit messages */
#define UHUB_EVENT_USER_JOIN 0x1001
#define UHUB_EVENT_USER_QUIT 0x1002
#define UHUB_EVENT_USER_DESTROY 0x1003
/* Send a broadcast message */
#define UHUB_EVENT_BROADCAST 0x2000
/* Statistics, OOM, reconfigure */
#define UHUB_EVENT_STATISTICS 0x4000
#define UHUB_EVENT_OUT_OF_MEMORY 0x4001
#define UHUB_EVENT_RECONFIGURE 0x4002
#endif /* HAVE_UHUB_EVENT_ID_H */

144
src/core/eventqueue.c Normal file
View File

@@ -0,0 +1,144 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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 EQ_DEBUG
static void eq_debug(const char* prefix, struct event_data* data)
{
printf(">>> %s: %p, id: %x, flags=%d\n", prefix, data, data->id, data->flags);
}
#endif
int event_queue_initialize(struct event_queue** queue, event_queue_callback callback, void* ptr)
{
*queue = (struct event_queue*) hub_malloc_zero(sizeof(struct event_queue));
if (!(*queue))
return -1;
(*queue)->q1 = list_create();
(*queue)->q2 = list_create();
if (!(*queue)->q1 || !(*queue)->q2)
{
list_destroy((*queue)->q1);
list_destroy((*queue)->q2);
return -1;
}
(*queue)->callback = callback;
(*queue)->callback_data = ptr;
return 0;
}
void event_queue_shutdown(struct event_queue* queue)
{
/* Should be empty at this point! */
list_destroy(queue->q1);
list_destroy(queue->q2);
hub_free(queue);
}
static void event_queue_cleanup_callback(void* ptr)
{
#ifdef EQ_DEBUG
struct event_data* data = (struct event_data*) ptr;
eq_debug("NUKE", data);
#endif
hub_free((struct event_data*) ptr);
}
int event_queue_process(struct event_queue* queue)
{
struct event_data* data;
if (queue->locked)
return 0;
/* lock primary queue, and handle the primary queue messages. */
queue->locked = 1;
data = (struct event_data*) list_get_first(queue->q1);
while (data)
{
#ifdef EQ_DEBUG
eq_debug("EXEC", data);
#endif
queue->callback(queue->callback_data, data);
data = (struct event_data*) list_get_next(queue->q1);
}
list_clear(queue->q1, event_queue_cleanup_callback);
assert(list_size(queue->q1) == 0);
/* unlock queue */
queue->locked = 0;
/* transfer from secondary queue to the primary queue. */
data = (struct event_data*) list_get_first(queue->q2);
while (data)
{
list_remove(queue->q2, data);
list_append(queue->q1, data);
data = (struct event_data*) list_get_first(queue->q2);
}
/* if more events exist, schedule it */
if (list_size(queue->q1))
{
return 1;
}
return 0;
}
void event_queue_post(struct event_queue* queue, struct event_data* message)
{
struct linked_list* q = (!queue->locked) ? queue->q1 : queue->q2;
struct event_data* data;
data = (struct event_data*) hub_malloc(sizeof(struct event_data));
if (data)
{
data->id = message->id;
data->ptr = message->ptr;
data->flags = message->flags;
#ifdef EQ_DEBUG
eq_debug("POST", data);
#endif
list_append(q, data);
}
else
{
hub_log(log_error, "event_queue_post: OUT OF MEMORY");
}
}
size_t event_queue_size(struct event_queue* queue)
{
return list_size(queue->q1) + list_size(queue->q2);
}

48
src/core/eventqueue.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_EVENT_QUEUE_H
#define HAVE_UHUB_EVENT_QUEUE_H
struct event_data
{
int id;
void* ptr;
int flags;
};
typedef void (*event_queue_callback)(void* callback_data, struct event_data* event_data);
struct event_queue
{
int locked;
struct linked_list* q1; /* primary */
struct linked_list* q2; /* secondary, when primary is locked */
event_queue_callback callback;
void* callback_data;
};
extern int event_queue_initialize(struct event_queue** queue, event_queue_callback callback, void* ptr);
extern int event_queue_process(struct event_queue* queue);
extern void event_queue_shutdown(struct event_queue* queue);
extern void event_queue_post(struct event_queue* queue, struct event_data* message);
extern size_t event_queue_size(struct event_queue* queue);
#endif /* HAVE_UHUB_EVENT_QUEUE_H */

959
src/core/hub.c Normal file
View File

@@ -0,0 +1,959 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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"
struct hub_info* g_hub = 0;
#define NETWORK_DUMP_DEBUG 1
int hub_handle_message(struct hub_info* hub, struct user* u, const char* line, size_t length)
{
int ret = 0;
struct adc_message* cmd = 0;
#ifdef NETWORK_DUMP_DEBUG
hub_log(log_protocol, "recv %s: %s", sid_to_string(u->id.sid), line);
#endif
if (user_is_disconnecting(u))
return -1;
cmd = adc_msg_parse_verify(u, line, length);
if (cmd)
{
switch (cmd->cmd)
{
case ADC_CMD_HSUP: ret = hub_handle_support(hub, u, cmd); break;
case ADC_CMD_HPAS: ret = hub_handle_password(hub, u, cmd); break;
case ADC_CMD_BINF: ret = hub_handle_info(hub, u, cmd); break;
case ADC_CMD_DINF:
case ADC_CMD_EINF:
case ADC_CMD_FINF:
/* these must never be allowed for security reasons,
so we ignore them. */
break;
case ADC_CMD_EMSG:
case ADC_CMD_DMSG:
case ADC_CMD_BMSG:
case ADC_CMD_FMSG:
ret = hub_handle_chat_message(hub, u, cmd);
break;
case ADC_CMD_BSCH:
case ADC_CMD_DSCH:
case ADC_CMD_ESCH:
case ADC_CMD_FSCH:
case ADC_CMD_DRES:
case ADC_CMD_DRCM:
case ADC_CMD_DCTM:
cmd->priority = -1;
if (hub->config->chat_only && u->credentials < cred_operator)
{
/* These below aren't allowed in chat only hubs */
break;
}
default:
if (user_is_logged_in(u))
{
ret = route_message(hub, u, cmd);
}
else
{
ret = -1;
}
break;
}
adc_msg_free(cmd);
}
else
{
if (!user_is_logged_in(u))
{
ret = -1;
}
}
return ret;
}
int hub_handle_support(struct hub_info* hub, struct user* u, struct adc_message* cmd)
{
int ret = 0;
int index = 0;
int ok = 1;
char* arg = adc_msg_get_argument(cmd, index);
if (hub->status == hub_status_disabled && u->state == state_protocol)
{
on_login_failure(hub, u, status_msg_hub_disabled);
return -1;
}
while (arg)
{
if (strlen(arg) == 6)
{
fourcc_t fourcc = FOURCC(arg[2], arg[3], arg[4], arg[5]);
if (strncmp(arg, ADC_SUP_FLAG_ADD, 2) == 0)
{
user_support_add(u, fourcc);
}
else if (strncmp(arg, ADC_SUP_FLAG_REMOVE, 2) == 0)
{
user_support_remove(u, fourcc);
}
else
{
ok = 0;
}
}
else
{
ok = 0;
}
index++;
hub_free(arg);
arg = adc_msg_get_argument(cmd, index);
}
if (u->state == state_protocol)
{
if (index == 0) ok = 0; /* Need to support *SOMETHING*, at least BASE */
if (ok)
{
hub_send_handshake(hub, u);
user_set_timeout(u, TIMEOUT_HANDSHAKE);
}
else
{
/* disconnect user. Do not send crap during initial handshake! */
hub_disconnect_user(hub, u, quit_logon_error);
ret = -1;
}
}
return ret;
}
int hub_handle_password(struct hub_info* hub, struct user* u, struct adc_message* cmd)
{
char* password = adc_msg_get_argument(cmd, 0);
int ret = 0;
if (u->state == state_verify)
{
if (acl_password_verify(hub->acl, u, password))
{
on_login_success(hub, u);
}
else
{
on_login_failure(hub, u, status_msg_auth_invalid_password);
ret = -1;
}
}
hub_free(password);
return ret;
}
int hub_handle_chat_message(struct hub_info* hub, struct user* u, struct adc_message* cmd)
{
char* message = adc_msg_get_argument(cmd, 0);
int ret = 0;
int relay = 1;
if (message[0] == '!' || message[0] == '+')
{
relay = command_dipatcher(hub, u, message);
}
if (hub->config->chat_is_privileged && !user_is_protected(u) && (cmd->cache[0] == 'B' || cmd->cache[0] == 'F'))
{
relay = 0;
}
if (relay && user_is_logged_in(u))
{
/* adc_msg_remove_named_argument(cmd, "PM"); */
ret = route_message(hub, u, cmd);
}
free(message);
return ret;
}
void hub_send_support(struct hub_info* hub, struct user* u)
{
if (user_is_connecting(u) || user_is_logged_in(u))
{
route_to_user(hub, u, hub->command_support);
}
}
void hub_send_sid(struct hub_info* hub, struct user* u)
{
struct adc_message* command;
if (user_is_connecting(u))
{
command = adc_msg_construct(ADC_CMD_ISID, 10);
u->id.sid = uman_get_free_sid(hub);
adc_msg_add_argument(command, (const char*) sid_to_string(u->id.sid));
route_to_user(hub, u, command);
adc_msg_free(command);
}
}
void hub_send_ping(struct hub_info* hub, struct user* user)
{
/* This will just send a newline, despite appearing to do more below. */
struct adc_message* ping = adc_msg_construct(0, 0);
ping->cache[0] = '\n';
ping->cache[1] = 0;
ping->length = 1;
ping->priority = 1;
route_to_user(hub, user, ping);
adc_msg_free(ping);
}
void hub_send_hubinfo(struct hub_info* hub, struct user* u)
{
struct adc_message* info = adc_msg_copy(hub->command_info);
int value = 0;
if (user_flag_get(u, feature_ping))
{
/*
FIXME: These are missing:
HH - Hub Host address ( DNS or IP )
WS - Hub Website
NE - Hub Network
OW - Hub Owner name
*/
adc_msg_add_named_argument(info, "UC", uhub_itoa(hub_get_user_count(hub)));
adc_msg_add_named_argument(info, "MC", uhub_itoa(hub_get_max_user_count(hub)));
adc_msg_add_named_argument(info, "SS", uhub_ulltoa(hub_get_shared_size(hub)));
adc_msg_add_named_argument(info, "SF", uhub_itoa(hub_get_shared_files(hub)));
/* Maximum/minimum share size */
value = hub_get_max_share(hub);
if (value) adc_msg_add_named_argument(info, "XS", uhub_itoa(value));
value = hub_get_min_share(hub);
if (value) adc_msg_add_named_argument(info, "MS", uhub_itoa(value));
/* Maximum/minimum upload slots allowed per user */
value = hub_get_max_slots(hub);
if (value) adc_msg_add_named_argument(info, "XL", uhub_itoa(value));
value = hub_get_min_slots(hub);
if (value) adc_msg_add_named_argument(info, "ML", uhub_itoa(value));
/* guest users must be on min/max hubs */
value = hub_get_max_hubs_user(hub);
if (value) adc_msg_add_named_argument(info, "XU", uhub_itoa(value));
value = hub_get_min_hubs_user(hub);
if (value) adc_msg_add_named_argument(info, "MU", uhub_itoa(value));
/* registered users must be on min/max hubs */
value = hub_get_max_hubs_reg(hub);
if (value) adc_msg_add_named_argument(info, "XR", uhub_itoa(value));
value = hub_get_min_hubs_reg(hub);
if (value) adc_msg_add_named_argument(info, "MR", uhub_itoa(value));
/* operators must be on min/max hubs */
value = hub_get_max_hubs_op(hub);
if (value) adc_msg_add_named_argument(info, "XO", uhub_itoa(value));
value = hub_get_min_hubs_op(hub);
if (value) adc_msg_add_named_argument(info, "MO", uhub_itoa(value));
/* uptime in seconds */
adc_msg_add_named_argument(info, "UP", uhub_itoa((int) difftime(time(0), hub->tm_started)));
}
if (user_is_connecting(u) || user_is_logged_in(u))
{
route_to_user(hub, u, info);
}
adc_msg_free(info);
/* Only send banner when connecting */
if (hub->config->show_banner && user_is_connecting(u))
{
route_to_user(hub, u, hub->command_banner);
}
}
void hub_send_handshake(struct hub_info* hub, struct user* u)
{
user_flag_set(u, flag_pipeline);
hub_send_support(hub, u);
hub_send_sid(hub, u);
hub_send_hubinfo(hub, u);
route_flush_pipeline(hub, u);
if (!user_is_disconnecting(u))
{
user_set_state(u, state_identify);
}
}
void hub_send_motd(struct hub_info* hub, struct user* u)
{
if (hub->command_motd)
{
route_to_user(hub, u, hub->command_motd);
}
}
void hub_send_password_challenge(struct hub_info* hub, struct user* u)
{
struct adc_message* igpa;
igpa = adc_msg_construct(ADC_CMD_IGPA, 38);
adc_msg_add_argument(igpa, acl_password_generate_challenge(hub->acl, u));
user_set_state(u, state_verify);
route_to_user(hub, u, igpa);
adc_msg_free(igpa);
}
static void hub_event_dispatcher(void* callback_data, struct event_data* message)
{
struct hub_info* hub = (struct hub_info*) callback_data;
struct user* user = (struct user*) message->ptr;
assert(hub != NULL);
switch (message->id)
{
case UHUB_EVENT_USER_JOIN:
{
if (user_is_disconnecting(user))
break;
if (message->flags)
{
hub_send_password_challenge(hub, user);
}
else
{
on_login_success(hub, user);
}
break;
}
case UHUB_EVENT_USER_QUIT:
{
uman_remove(hub, user);
uman_send_quit_message(hub, user);
on_logout_user(hub, user);
hub_schedule_destroy_user(hub, user);
break;
}
case UHUB_EVENT_USER_DESTROY:
{
user_destroy(user);
break;
}
default:
/* FIXME: ignored */
break;
}
}
struct hub_info* hub_start_service(struct hub_config* config)
{
struct hub_info* hub = 0;
struct sockaddr_storage addr;
socklen_t sockaddr_size;
int server_tcp, ret, ipv6_supported, af;
char address_buf[INET6_ADDRSTRLEN+1];
hub = hub_malloc_zero(sizeof(struct hub_info));
if (!hub)
{
hub_log(log_fatal, "Unable to allocate memory for hub");
return 0;
}
hub->tm_started = time(0);
ipv6_supported = net_is_ipv6_supported();
if (ipv6_supported)
hub_log(log_debug, "IPv6 supported.");
else
hub_log(log_debug, "IPv6 not supported.");
if (ip_convert_address(config->server_bind_addr, config->server_port, (struct sockaddr*) &addr, &sockaddr_size) == -1)
{
hub_free(hub);
return 0;
}
af = addr.ss_family;
if (af == AF_INET)
{
net_address_to_string(AF_INET, &((struct sockaddr_in*) &addr)->sin_addr, address_buf, INET6_ADDRSTRLEN);
}
else if (af == AF_INET6)
{
net_address_to_string(AF_INET6, &((struct sockaddr_in6*) &addr)->sin6_addr, address_buf, INET6_ADDRSTRLEN);
}
#ifdef LIBEVENT_1_4
hub->evbase = event_base_new();
#else
hub->evbase = event_init();
#endif
if (!hub->evbase)
{
hub_log(log_error, "Unable to initialize libevent.");
hub_free(hub);
return 0;
}
hub_log(log_info, "Starting " PRODUCT "/" VERSION ", listening on %s:%d...", address_buf, config->server_port);
hub_log(log_debug, "Using libevent %s, backend: %s", event_get_version(), event_get_method());
server_tcp = net_socket_create(af, SOCK_STREAM, IPPROTO_TCP);
if (server_tcp == -1)
{
event_base_free(hub->evbase);
hub_free(hub);
return 0;
}
ret = net_set_reuseaddress(server_tcp, 1);
if (ret == -1)
{
event_base_free(hub->evbase);
hub_free(hub);
net_close(server_tcp);
return 0;
}
ret = net_set_nonblocking(server_tcp, 1);
if (ret == -1)
{
event_base_free(hub->evbase);
hub_free(hub);
net_close(server_tcp);
return 0;
}
ret = net_bind(server_tcp, (struct sockaddr*) &addr, sockaddr_size);
if (ret == -1)
{
hub_log(log_fatal, "hub_start_service(): Unable to bind to TCP local address. errno=%d, str=%s", net_error(), net_error_string(net_error()));
event_base_free(hub->evbase);
hub_free(hub);
net_close(server_tcp);
return 0;
}
ret = net_listen(server_tcp, SERVER_BACKLOG);
if (ret == -1)
{
hub_log(log_fatal, "hub_start_service(): Unable to listen to socket");
event_base_free(hub->evbase);
hub_free(hub);
net_close(server_tcp);
return 0;
}
hub->fd_tcp = server_tcp;
hub->config = config;
hub->users = NULL;
if (uman_init(hub) == -1)
{
hub_free(hub);
net_close(server_tcp);
return 0;
}
event_set(&hub->ev_accept, hub->fd_tcp, EV_READ | EV_PERSIST, net_on_accept, hub);
event_base_set(hub->evbase, &hub->ev_accept);
if (event_add(&hub->ev_accept, NULL) == -1)
{
uman_shutdown(hub);
hub_free(hub);
net_close(server_tcp);
return 0;
}
if (event_queue_initialize(&hub->queue, hub_event_dispatcher, (void*) hub) == -1)
{
uman_shutdown(hub);
hub_free(hub);
net_close(server_tcp);
return 0;
}
hub->recvbuf = hub_malloc(MAX_RECV_BUF);
hub->sendbuf = hub_malloc(MAX_SEND_BUF);
if (!hub->recvbuf || !hub->sendbuf)
{
hub_free(hub->recvbuf);
hub_free(hub->sendbuf);
uman_shutdown(hub);
hub_free(hub);
net_close(server_tcp);
return 0;
}
hub->status = hub_status_running;
g_hub = hub;
return hub;
}
void hub_shutdown_service(struct hub_info* hub)
{
hub_log(log_trace, "hub_shutdown_service()");
event_queue_shutdown(hub->queue);
event_del(&hub->ev_accept);
net_close(hub->fd_tcp);
uman_shutdown(hub);
hub->status = hub_status_stopped;
event_base_free(hub->evbase);
hub_free(hub->sendbuf);
hub_free(hub->recvbuf);
hub_free(hub);
hub = 0;
g_hub = 0;
}
#define SERVER "" PRODUCT "/" VERSION ""
void hub_set_variables(struct hub_info* hub, struct acl_handle* acl)
{
int fd, ret;
char buf[MAX_RECV_BUF];
char* tmp;
char* server = adc_msg_escape(SERVER); /* FIXME: OOM */
hub->acl = acl;
hub->command_info = adc_msg_construct(ADC_CMD_IINF, 15 + strlen(server));
if (hub->command_info)
{
adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_CLIENT_TYPE, ADC_CLIENT_TYPE_HUB);
adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_USER_AGENT, server);
tmp = adc_msg_escape(hub->config->hub_name);
adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_NICK, tmp);
hub_free(tmp);
tmp = adc_msg_escape(hub->config->hub_description);
adc_msg_add_named_argument(hub->command_info, ADC_INF_FLAG_DESCRIPTION, tmp);
hub_free(tmp);
}
/* (Re-)read the message of the day */
hub->command_motd = 0;
fd = open(hub->config->file_motd, 0);
if (fd != -1)
{
ret = read(fd, buf, MAX_RECV_BUF);
if (ret > 0)
{
buf[ret] = 0;
tmp = adc_msg_escape(buf);
hub->command_motd = adc_msg_construct(ADC_CMD_IMSG, 6 + strlen(tmp));
adc_msg_add_argument(hub->command_motd, tmp);
hub_free(tmp);
}
else
{
}
close(fd);
}
hub->command_support = adc_msg_construct(ADC_CMD_ISUP, 6 + strlen(ADC_PROTO_SUPPORT));
if (hub->command_support)
{
adc_msg_add_argument(hub->command_support, ADC_PROTO_SUPPORT);
}
hub->command_banner = adc_msg_construct(ADC_CMD_ISTA, 25 + strlen(server));
if (hub->command_banner)
{
tmp = adc_msg_escape("Powered by " SERVER);
adc_msg_add_argument(hub->command_banner, "000");
adc_msg_add_argument(hub->command_banner, tmp);
hub_free(tmp);
}
hub->status = (hub->config->hub_enabled ? hub_status_running : hub_status_disabled);
hub_free(server);
}
void hub_free_variables(struct hub_info* hub)
{
adc_msg_free(hub->command_info);
adc_msg_free(hub->command_banner);
if (hub->command_motd)
adc_msg_free(hub->command_motd);
adc_msg_free(hub->command_support);
}
/**
* @return 1 if nickname is in use, or 0 if not used.
*/
static inline int is_nick_in_use(struct hub_info* hub, const char* nick)
{
struct user* lookup = uman_get_user_by_nick(hub, nick);
if (lookup)
{
return 1;
}
return 0;
}
/**
* @return 1 if CID is in use, or 0 if not used.
*/
static inline int is_cid_in_use(struct hub_info* hub, const char* cid)
{
struct user* lookup = uman_get_user_by_cid(hub, cid);
if (lookup)
{
return 1;
}
return 0;
}
static void set_status_code(enum msg_status_level level, int code, char buffer[4])
{
buffer[0] = ('0' + (int) level);
buffer[1] = ('0' + (code / 10));
buffer[2] = ('0' + (code % 10));
buffer[3] = 0;
}
/**
* @param hub The hub instance this message is sent from.
* @param user The user this message is sent to.
* @param msg See enum status_message
* @param level See enum status_level
*/
void hub_send_status(struct hub_info* hub, struct user* user, enum status_message msg, enum msg_status_level level)
{
struct hub_config* cfg = hub->config;
struct adc_message* cmd = adc_msg_construct(ADC_CMD_ISTA, 6);
if (!cmd) return;
char code[4];
const char* text = 0;
const char* flag = 0;
char* escaped_text = 0;
#define STATUS(CODE, MSG, FLAG) case status_ ## MSG : set_status_code(level, CODE, code); text = cfg->MSG; flag = FLAG; break
switch (msg)
{
STATUS(11, msg_hub_full, 0);
STATUS(12, msg_hub_disabled, 0);
STATUS(26, msg_hub_registered_users_only, 0);
STATUS(43, msg_inf_error_nick_missing, 0);
STATUS(43, msg_inf_error_nick_multiple, 0);
STATUS(21, msg_inf_error_nick_invalid, 0);
STATUS(21, msg_inf_error_nick_long, 0);
STATUS(21, msg_inf_error_nick_short, 0);
STATUS(21, msg_inf_error_nick_spaces, 0);
STATUS(21, msg_inf_error_nick_bad_chars, 0);
STATUS(21, msg_inf_error_nick_not_utf8, 0);
STATUS(22, msg_inf_error_nick_taken, 0);
STATUS(21, msg_inf_error_nick_restricted, 0);
STATUS(43, msg_inf_error_cid_invalid, "FBID");
STATUS(43, msg_inf_error_cid_missing, "FMID");
STATUS(24, msg_inf_error_cid_taken, 0);
STATUS(43, msg_inf_error_pid_missing, "FMPD");
STATUS(27, msg_inf_error_pid_invalid, "FBPD");
STATUS(31, msg_ban_permanently, 0);
STATUS(32, msg_ban_temporarily, "TL600"); /* FIXME: Use a proper timeout */
STATUS(23, msg_auth_invalid_password, 0);
STATUS(20, msg_auth_user_not_found, 0);
STATUS(30, msg_error_no_memory, 0);
STATUS(43, msg_user_share_size_low, "FB" ADC_INF_FLAG_SHARED_SIZE);
STATUS(43, msg_user_share_size_high, "FB" ADC_INF_FLAG_SHARED_SIZE);
STATUS(43, msg_user_slots_low, "FB" ADC_INF_FLAG_UPLOAD_SLOTS);
STATUS(43, msg_user_slots_high, "FB" ADC_INF_FLAG_UPLOAD_SLOTS);
STATUS(43, msg_user_hub_limit_low, 0);
STATUS(43, msg_user_hub_limit_high, 0);
}
#undef STATUS
escaped_text = adc_msg_escape(text);
adc_msg_add_argument(cmd, code);
adc_msg_add_argument(cmd, escaped_text);
hub_free(escaped_text);
if (flag)
{
adc_msg_add_argument(cmd, flag);
}
route_to_user(hub, user, cmd);
adc_msg_free(cmd);
}
const char* hub_get_status_message(struct hub_info* hub, enum status_message msg)
{
#define STATUS(MSG) case status_ ## MSG : return cfg->MSG; break
struct hub_config* cfg = hub->config;
switch (msg)
{
STATUS(msg_hub_full);
STATUS(msg_hub_disabled);
STATUS(msg_hub_registered_users_only);
STATUS(msg_inf_error_nick_missing);
STATUS(msg_inf_error_nick_multiple);
STATUS(msg_inf_error_nick_invalid);
STATUS(msg_inf_error_nick_long);
STATUS(msg_inf_error_nick_short);
STATUS(msg_inf_error_nick_spaces);
STATUS(msg_inf_error_nick_bad_chars);
STATUS(msg_inf_error_nick_not_utf8);
STATUS(msg_inf_error_nick_taken);
STATUS(msg_inf_error_nick_restricted);
STATUS(msg_inf_error_cid_invalid);
STATUS(msg_inf_error_cid_missing);
STATUS(msg_inf_error_cid_taken);
STATUS(msg_inf_error_pid_missing);
STATUS(msg_inf_error_pid_invalid);
STATUS(msg_ban_permanently);
STATUS(msg_ban_temporarily);
STATUS(msg_auth_invalid_password);
STATUS(msg_auth_user_not_found);
STATUS(msg_error_no_memory);
STATUS(msg_user_share_size_low);
STATUS(msg_user_share_size_high);
STATUS(msg_user_slots_low);
STATUS(msg_user_slots_high);
STATUS(msg_user_hub_limit_low);
STATUS(msg_user_hub_limit_high);
}
#undef STATUS
return "Unknown";
}
const char* hub_get_status_message_log(struct hub_info* hub, enum status_message msg)
{
#define STATUS(MSG) case status_ ## MSG : return #MSG; break
switch (msg)
{
STATUS(msg_hub_full);
STATUS(msg_hub_disabled);
STATUS(msg_hub_registered_users_only);
STATUS(msg_inf_error_nick_missing);
STATUS(msg_inf_error_nick_multiple);
STATUS(msg_inf_error_nick_invalid);
STATUS(msg_inf_error_nick_long);
STATUS(msg_inf_error_nick_short);
STATUS(msg_inf_error_nick_spaces);
STATUS(msg_inf_error_nick_bad_chars);
STATUS(msg_inf_error_nick_not_utf8);
STATUS(msg_inf_error_nick_taken);
STATUS(msg_inf_error_nick_restricted);
STATUS(msg_inf_error_cid_invalid);
STATUS(msg_inf_error_cid_missing);
STATUS(msg_inf_error_cid_taken);
STATUS(msg_inf_error_pid_missing);
STATUS(msg_inf_error_pid_invalid);
STATUS(msg_ban_permanently);
STATUS(msg_ban_temporarily);
STATUS(msg_auth_invalid_password);
STATUS(msg_auth_user_not_found);
STATUS(msg_error_no_memory);
STATUS(msg_user_share_size_low);
STATUS(msg_user_share_size_high);
STATUS(msg_user_slots_low);
STATUS(msg_user_slots_high);
STATUS(msg_user_hub_limit_low);
STATUS(msg_user_hub_limit_high);
}
#undef STATUS
return "unknown";
}
size_t hub_get_user_count(struct hub_info* hub)
{
return hub->users->count;
}
size_t hub_get_max_user_count(struct hub_info* hub)
{
return hub->config->max_users;
}
uint64_t hub_get_shared_size(struct hub_info* hub)
{
return hub->users->shared_size;
}
uint64_t hub_get_shared_files(struct hub_info* hub)
{
return hub->users->shared_files;
}
uint64_t hub_get_min_share(struct hub_info* hub)
{
return 1024 * 1024 * hub->config->limit_min_share;
}
uint64_t hub_get_max_share(struct hub_info* hub)
{
return 1024 * 1024 * hub->config->limit_max_share;
}
size_t hub_get_min_slots(struct hub_info* hub)
{
return hub->config->limit_min_slots;
}
size_t hub_get_max_slots(struct hub_info* hub)
{
return hub->config->limit_max_slots;
}
size_t hub_get_max_hubs_total(struct hub_info* hub)
{
return hub->config->limit_max_hubs;
}
size_t hub_get_max_hubs_user(struct hub_info* hub)
{
return hub->config->limit_max_hubs_user;
}
size_t hub_get_min_hubs_user(struct hub_info* hub)
{
return hub->config->limit_min_hubs_user;
}
size_t hub_get_max_hubs_reg(struct hub_info* hub)
{
return hub->config->limit_max_hubs_reg;
}
size_t hub_get_min_hubs_reg(struct hub_info* hub)
{
return hub->config->limit_min_hubs_reg;
}
size_t hub_get_max_hubs_op(struct hub_info* hub)
{
return hub->config->limit_max_hubs_op;
}
size_t hub_get_min_hubs_op(struct hub_info* hub)
{
return hub->config->limit_min_hubs_op;
}
void hub_event_loop(struct hub_info* hub)
{
int ret;
do
{
ret = event_base_loop(hub->evbase, EVLOOP_ONCE);
if (ret != 0)
{
hub_log(log_debug, "event_base_loop returned: %d", (int) ret);
}
if (ret < 0)
break;
event_queue_process(hub->queue);
}
while (hub->status == hub_status_running || hub->status == hub_status_disabled);
}
void hub_schedule_destroy_user(struct hub_info* hub, struct user* user)
{
struct event_data post;
memset(&post, 0, sizeof(post));
post.id = UHUB_EVENT_USER_DESTROY;
post.ptr = user;
event_queue_post(hub->queue, &post);
}
void hub_disconnect_user(struct hub_info* hub, struct user* user, int reason)
{
struct event_data post;
int need_notify = 0;
/* is user already being disconnected ? */
if (user_is_disconnecting(user))
{
return;
}
/* stop reading from user */
net_shutdown_r(user->net.sd);
hub_log(log_trace, "hub_disconnect_user(), user=%p, reason=%d, state=%d", user, reason, user->state);
need_notify = user_is_logged_in(user);
user->quit_reason = reason;
user_set_state(user, state_cleanup);
if (need_notify)
{
memset(&post, 0, sizeof(post));
post.id = UHUB_EVENT_USER_QUIT;
post.ptr = user;
event_queue_post(hub->queue, &post);
}
else
{
user->quit_reason = quit_unknown;
hub_schedule_destroy_user(hub, user);
}
}

336
src/core/hub.h Normal file
View File

@@ -0,0 +1,336 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_HUB_H
#define HAVE_UHUB_HUB_H
enum status_message
{
status_msg_hub_full = -1, /* hub is full */
status_msg_hub_disabled = -2, /* hub is disabled */
status_msg_hub_registered_users_only = -3, /* hub is for registered users only */
status_msg_inf_error_nick_missing = -4, /* no nickname given */
status_msg_inf_error_nick_multiple = -5, /* multiple nicknames given */
status_msg_inf_error_nick_invalid = -6, /* generic/unkown */
status_msg_inf_error_nick_long = -7, /* nickname too long */
status_msg_inf_error_nick_short = -8, /* nickname too short */
status_msg_inf_error_nick_spaces = -9, /* nickname cannot start with spaces */
status_msg_inf_error_nick_bad_chars = -10, /* nickname contains chars below ascii 32 */
status_msg_inf_error_nick_not_utf8 = -11, /* nickname is not valid utf8 */
status_msg_inf_error_nick_taken = -12, /* nickname is in use */
status_msg_inf_error_nick_restricted = -13, /* nickname cannot be used on this hub */
status_msg_inf_error_cid_invalid = -14, /* CID is not valid (generic error) */
status_msg_inf_error_cid_missing = -15, /* CID is not specified */
status_msg_inf_error_cid_taken = -16, /* CID is taken (already logged in?). */
status_msg_inf_error_pid_missing = -17, /* PID is not specified */
status_msg_inf_error_pid_invalid = -18, /* PID is invalid */
status_msg_ban_permanently = -19, /* Banned permanently */
status_msg_ban_temporarily = -20, /* Banned temporarily */
status_msg_auth_invalid_password = -21, /* Password is wrong */
status_msg_auth_user_not_found = -22, /* User not found in password database */
status_msg_error_no_memory = -23, /* Hub is out of memory */
status_msg_user_share_size_low = -40, /* User is not sharing enough. */
status_msg_user_share_size_high = -41, /* User is sharing too much. */
status_msg_user_slots_low = -42, /* User has too few slots open. */
status_msg_user_slots_high = -43, /* User has too many slots open. */
status_msg_user_hub_limit_low = -44, /* Use is on too few hubs. */
status_msg_user_hub_limit_high = -45, /* Use is on too many hubs. */
};
enum hub_state
{
hub_status_uninitialized = 0, /**<<<"Hub is uninitialized" */
hub_status_running = 1, /**<<<"Hub is running (normal operation)" */
hub_status_restart = 2, /**<<<"Hub is restarting (re-reading configuration, etc)" */
hub_status_shutdown = 3, /**<<<"Hub is shutting down, but not yet stopped. */
hub_status_stopped = 4, /**<<<"Hub is stopped (Pretty much the same as initialized) */
hub_status_disabled = 5, /**<<<"Hub is disabled (Running, but not accepting users) */
};
/**
* Always updated each minute.
*/
struct hub_stats
{
size_t net_tx;
size_t net_rx;
size_t net_tx_peak;
size_t net_rx_peak;
size_t net_tx_total;
size_t net_rx_total;
};
struct hub_info
{
int fd_tcp;
struct event ev_accept;
struct event ev_timer;
struct hub_stats stats;
struct event_queue* queue;
struct event_base* evbase;
struct hub_config* config;
struct user_manager* users;
struct acl_handle* acl;
struct adc_message* command_info; /* The hub's INF command */
struct adc_message* command_support; /* The hub's SUP command */
struct adc_message* command_motd; /* The message of the day */
struct adc_message* command_banner; /* The default welcome message */
time_t tm_started;
int status;
char* recvbuf; /* Global receive buffer */
char* sendbuf; /* Global send buffer */
#ifdef SSL_SUPPORT
SSL_METHOD* ssl_method;
SSL_CTX* ssl_ctx;
#endif /* SSL_SUPPORT */
};
/**
* This is the message pre-routing centre.
*
* Any message coming in to the hub comes through here first,
* and will be routed further if valid.
*
* @return 0 on success, -1 on error
*/
extern int hub_handle_message(struct hub_info* hub, struct user* u, const char* message, size_t length);
/**
* Handle protocol support/subscription messages received clients.
*
* @return 0 on success, -1 on error
*/
extern int hub_handle_support(struct hub_info* hub, struct user* u, struct adc_message* cmd);
/**
* Handle password messages received from clients.
*
* @return 0 on success, -1 on error
*/
extern int hub_handle_password(struct hub_info* hub, struct user* u, struct adc_message* cmd);
/**
* Handle chat messages received from clients.
* @return 0 on success, -1 on error.
*/
extern int hub_handle_chat_message(struct hub_info* hub, struct user* u, struct adc_message* cmd);
/**
* Used internally by hub_handle_info
* @return 1 if nickname is OK, or 0 if nickname is not accepted.
*/
extern int hub_handle_info_check_nick(struct hub_info* hub, struct user* u, struct adc_message* cmd);
/**
* Used internally by hub_handle_info
* @return 1 if CID/PID is OK, or 0 if not valid.
*/
extern int hub_handle_info_check_cid(struct hub_info* hub, struct user* u, struct adc_message* cmd);
/**
* Send the support line for the hub to a particular user.
* Only used during the initial handshake.
*/
extern void hub_send_support(struct hub_info* hub, struct user* u);
/**
* Send a message assigning a SID for a user.
* This is only sent after hub_send_support() during initial handshake.
*/
extern void hub_send_sid(struct hub_info* hub, struct user* u);
/**
* Send a 'ping' message to user.
*/
extern void hub_send_ping(struct hub_info* hub, struct user* user);
/**
* Send a message containing hub information to a particular user.
* This is sent during user connection, but can safely be sent at any
* point later.
*/
extern void hub_send_hubinfo(struct hub_info* hub, struct user* u);
/**
* Send handshake. This basically calls
* hub_send_support() and hub_send_sid()
*/
extern void hub_send_handshake(struct hub_info* hub, struct user* u);
/**
* Send a welcome message containing the message of the day to
* one particular user. This can be sent in any point in time.
*/
extern void hub_send_motd(struct hub_info* hub, struct user* u);
/**
* Send a password challenge to a user.
* This is only used if the user tries to access the hub using a
* password protected nick name.
*/
extern void hub_send_password_challenge(struct hub_info* hub, struct user* u);
/**
* Sends a status_message to a user.
*/
extern void hub_send_status(struct hub_info*, struct user* user, enum status_message msg, enum msg_status_level level);
/**
* Allocates memory, initializes the hub based on the configuration,
* and returns a hub handle.
* This hub handle must be passed to hub_shutdown_service() in order to cleanup before exiting.
*
* @return a pointer to the hub info.
*/
extern struct hub_info* hub_start_service(struct hub_config* config);
/**
* This shuts down the hub.
*/
extern void hub_shutdown_service(struct hub_info* hub);
/**
* This configures the hub.
*/
extern void hub_set_variables(struct hub_info* hub, struct acl_handle* acl);
/**
* This frees the configuration of the hub.
*/
extern void hub_free_variables(struct hub_info* hub);
/**
* Returns a string for the given status_message (See enum status_message).
*/
extern const char* hub_get_status_message(struct hub_info* hub, enum status_message msg);
extern const char* hub_get_status_message_log(struct hub_info* hub, enum status_message msg);
/**
* Returns the number of logged in users on the hub.
*/
extern size_t hub_get_user_count(struct hub_info* hub);
/**
* Returns the maximum number of allowed users on the hub.
*/
extern size_t hub_get_max_user_count(struct hub_info* hub);
/**
* Returns the accumulated shared size for all logged in
* users on the hub.
*/
extern uint64_t hub_get_shared_size(struct hub_info* hub);
/**
* Returns the accumulated number of files for all logged
* in users on the hub.
*/
extern uint64_t hub_get_shared_files(struct hub_info* hub);
/**
* Returns the minimal share size limit as enforced by
* this hub's configuration.
*/
extern uint64_t hub_get_min_share(struct hub_info* hub);
/**
* Returns the minimal share size limit as enforced by
* this hub's configuration.
*/
extern uint64_t hub_get_max_share(struct hub_info* hub);
/**
* Returns the minimum upload slot limit as enforced by
* this hub's configuration.
* Users with fewer slots in total will not be allowed
* to enter the hub.
* @return limit or 0 if no limit.
*/
extern size_t hub_get_min_slots(struct hub_info* hub);
/**
* Returns the maximum upload slot limit as enforced by
* this hub's configuration.
* Users with more allowed upload slots will not be
* allowed to enter the hub.
* @return limit or 0 if no limit.
*/
extern size_t hub_get_max_slots(struct hub_info* hub);
/**
* Returns the maximum number of hubs a user can
* be logged in to simultaneously as a regular user (guest).
* Users on more hubs will not be allowed to stay on this hub.
* @return limit or 0 if no limit.
*/
extern size_t hub_get_max_hubs_user(struct hub_info* hub);
extern size_t hub_get_min_hubs_user(struct hub_info* hub);
/**
* Returns the maximum number of hubs a user can
* be logged in to simultaneously as a registered user (password required).
* Users on more hubs will not be allowed to stay on this hub.
* @return limit or 0 if no limit.
*/
extern size_t hub_get_max_hubs_reg(struct hub_info* hub);
extern size_t hub_get_min_hubs_reg(struct hub_info* hub);
/**
* Returns the maximum number of hubs a user can
* be logged in to simultaneously as an operator.
* Users who are operator on more than this amount of hubs
* will not be allowed to stay on this hub.
* @return limit or 0 if no limit.
*/
extern size_t hub_get_max_hubs_op(struct hub_info* hub);
extern size_t hub_get_min_hubs_op(struct hub_info* hub);
/**
* Returns the maximum number of hubs a user can
* be logged in to simultaneously regardless of the type of user.
*/
extern size_t hub_get_max_hubs_total(struct hub_info* hub);
/**
* Schedule runslice.
*/
extern void hub_schedule_runslice(struct hub_info* hub);
/**
* Run event loop.
*/
extern void hub_event_loop(struct hub_info* hub);
/**
* Schedule destroying a user.
*/
extern void hub_schedule_destroy_user(struct hub_info* hub, struct user* user);
/**
* Disconnect a user from the hub.
*/
extern void hub_disconnect_user(struct hub_info* hub, struct user* user, int reason);
#endif /* HAVE_UHUB_HUB_H */

119
src/core/hubevent.c Normal file
View File

@@ -0,0 +1,119 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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"
static void log_user_login(struct user* u)
{
const char* cred = get_user_credential_string(u->credentials);
const char* addr = ip_convert_to_string(&u->net.ipaddr);
hub_log(log_user, "LoginOK %s/%s %s \"%s\" (%s) \"%s\"", sid_to_string(u->id.sid), u->id.cid, addr, u->id.nick, cred, u->user_agent);
}
static void log_user_login_error(struct user* u, enum status_message msg)
{
const char* addr = ip_convert_to_string(&u->net.ipaddr);
const char* message = hub_get_status_message_log(u->hub, msg);
hub_log(log_user, "LoginError %s/%s %s \"%s\" (%s) \"%s\"", sid_to_string(u->id.sid), u->id.cid, addr, u->id.nick, message, u->user_agent);
}
static void log_user_logout(struct user* u, const char* message)
{
const char* addr = ip_convert_to_string(&u->net.ipaddr);
hub_log(log_user, "Logout %s/%s %s \"%s\" (%s)", sid_to_string(u->id.sid), u->id.cid, addr, u->id.nick, message);
}
static void log_user_nick_change(struct user* u, const char* nick)
{
const char* addr = ip_convert_to_string(&u->net.ipaddr);
hub_log(log_user, "NickChange %s/%s %s \"%s\" -> \"%s\"", sid_to_string(u->id.sid), u->id.cid, addr, u->id.nick, nick);
}
/* Send MOTD, do logging etc */
void on_login_success(struct hub_info* hub, struct user* u)
{
/* Send user list of all existing users */
if (!uman_send_user_list(hub, u))
return;
/* Mark as being in the normal state, and add user to the user list */
user_set_state(u, state_normal);
uman_add(hub, u);
/* Print log message */
log_user_login(u);
/* Announce new user to all connected users */
if (user_is_logged_in(u))
route_info_message(hub, u);
/* Send message of the day (if any) */
if (user_is_logged_in(u)) /* Previous send() can fail! */
hub_send_motd(hub, u);
/* reset to idle timeout */
user_set_timeout(u, TIMEOUT_IDLE);
}
void on_login_failure(struct hub_info* hub, struct user* u, enum status_message msg)
{
log_user_login_error(u, msg);
hub_send_status(hub, u, msg, status_level_fatal);
hub_disconnect_user(hub, u, quit_logon_error);
}
void on_nick_change(struct hub_info* hub, struct user* u, const char* nick)
{
if (user_is_logged_in(u))
{
log_user_nick_change(u, nick);
}
}
void on_logout_user(struct hub_info* hub, struct user* user)
{
const char* reason = "";
/* These are used for logging purposes */
switch (user->quit_reason)
{
case quit_disconnected: reason = "disconnected"; break;
case quit_kicked: reason = "kicked"; break;
case quit_banned: reason = "banned"; break;
case quit_timeout: reason = "timeout"; break;
case quit_send_queue: reason = "send queue"; break;
case quit_memory_error: reason = "out of memory"; break;
case quit_socket_error: reason = "socket error"; break;
case quit_protocol_error: reason = "protocol error"; break;
case quit_logon_error: reason = "login error"; break;
case quit_hub_disabled: reason = "hub disabled"; break;
case quit_ghost_timeout: reason = "ghost"; break;
default:
if (hub->status == hub_status_shutdown)
reason = "hub shutdown";
else
reason = "unknown error";
break;
}
log_user_logout(user, reason);
user->quit_reason = 0;
}

45
src/core/hubevent.h Normal file
View File

@@ -0,0 +1,45 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_HUB_EVENT_H
#define HAVE_UHUB_HUB_EVENT_H
/**
* This event is triggered whenever a user successfully logs in to the hub.
*/
extern void on_login_success(struct hub_info* hub, struct user* u);
/**
* This event is triggered whenever a user failed to log in to the hub.
*/
extern void on_login_failure(struct hub_info* hub, struct user* u, enum status_message msg);
/**
* This event is triggered whenever a previously logged in user leaves the hub.
*/
extern void on_logout_user(struct hub_info* hub, struct user* u);
/**
* This event is triggered whenever a user changes his/her nickname.
*/
extern void on_nick_change(struct hub_info* hub, struct user* u, const char* nick);
#endif /* HAVE_UHUB_HUB_EVENT_H */

213
src/core/hubio.c Normal file
View File

@@ -0,0 +1,213 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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 "hubio.h"
// #define SEND_CHUNKS 1
/* FIXME: This should not be needed! */
extern struct hub_info* g_hub;
#ifdef DEBUG_SENDQ
static void debug_msg(const char* prefix, struct adc_message* msg)
{
size_t n;
char* buf = strdup(msg->cache);
for (n = 0; n < msg->length; n++)
{
if (buf[n] == '\r' || buf[n] == '\n')
buf[n] = '_';
}
hub_log(log_trace, "%s: [%s] (%d bytes)", prefix, buf, (int) msg->length);
free(buf);
}
#endif
struct hub_recvq* hub_recvq_create()
{
struct hub_recvq* q = hub_malloc_zero(sizeof(struct hub_recvq));
return q;
}
void hub_recvq_destroy(struct hub_recvq* q)
{
if (q)
{
hub_free(q->buf);
hub_free(q);
}
}
size_t hub_recvq_get(struct hub_recvq* q, void* buf, size_t bufsize)
{
assert(bufsize >= q->size);
if (q->size)
{
size_t n = q->size;
memcpy(buf, q->buf, n);
hub_free(q->buf);
q->buf = 0;
q->size = 0;
return n;
}
return 0;
}
size_t hub_recvq_set(struct hub_recvq* q, void* buf, size_t bufsize)
{
if (q->buf)
{
hub_free(q->buf);
q->buf = 0;
q->size = 0;
}
if (!bufsize)
{
return 0;
}
q->buf = hub_malloc(bufsize);
if (!q->buf)
return 0;
q->size = bufsize;
memcpy(q->buf, buf, bufsize);
return bufsize;
}
struct hub_sendq* hub_sendq_create()
{
struct hub_sendq* q = hub_malloc_zero(sizeof(struct hub_sendq));
if (!q)
return 0;
q->queue = list_create();
if (!q->queue)
{
hub_free(q);
return 0;
}
return q;
}
static void clear_send_queue_callback(void* ptr)
{
adc_msg_free((struct adc_message*) ptr);
}
void hub_sendq_destroy(struct hub_sendq* q)
{
if (q)
{
list_clear(q->queue, &clear_send_queue_callback);
list_destroy(q->queue);
hub_free(q);
}
}
void hub_sendq_add(struct hub_sendq* q, struct adc_message* msg_)
{
struct adc_message* msg = adc_msg_incref(msg_);
#ifdef DEBUG_SENDQ
debug_msg("hub_sendq_add", msg);
#endif
list_append(q->queue, msg);
q->size += msg->length;
}
void hub_sendq_remove(struct hub_sendq* q, struct adc_message* msg)
{
#ifdef DEBUG_SENDQ
debug_msg("hub_sendq_remove", msg);
#endif
list_remove(q->queue, msg);
q->size -= msg->length;
adc_msg_free(msg);
q->offset = 0;
}
int hub_sendq_send(struct hub_sendq* q, hub_recvq_write w, void* data)
{
int ret = 0;
size_t bytes = 0;
size_t offset = q->offset; // offset into first message.
size_t remain = 0;
size_t length = 0;
char* sbuf = g_hub->sendbuf;
size_t max_send_buf = 4096;
/* Copy as many messages possible into global send queue */
struct adc_message* msg = list_get_first(q->queue);
while (msg)
{
length = MIN(msg->length - offset, (max_send_buf-1) - bytes);
memcpy(sbuf + bytes, msg->cache + offset, length);
bytes += length;
if (length < (msg->length - offset))
break;
offset = 0;
msg = list_get_next(q->queue);
}
msg = list_get_first(q->queue);
/* Send as much as possible */
ret = w(data, sbuf, bytes);
if (ret > 0)
{
#ifdef SSL_SUPPORT
q->last_write_n = ret;
#endif
/* Remove messages sent */
offset = q->offset;
remain = ret;
while (msg)
{
length = msg->length - offset;
if (length >= remain)
{
q->offset += remain;
break;
}
remain -= length;
hub_sendq_remove(q, msg);
msg = list_get_next(q->queue);
offset = 0;
}
}
return ret;
}
int hub_sendq_is_empty(struct hub_sendq* q)
{
return (q->size - q->offset) == 0;
}
size_t hub_sendq_get_bytes(struct hub_sendq* q)
{
return q->size - q->offset;
}

107
src/core/hubio.h Normal file
View File

@@ -0,0 +1,107 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_HUB_IO_H
#define HAVE_UHUB_HUB_IO_H
struct adc_message;
struct linked_list;
typedef int (*hub_recvq_write)(void* desc, const void* buf, size_t len);
typedef int (*hub_recvq_read)(void* desc, void* buf, size_t len);
struct hub_sendq
{
size_t size; /** Size of send queue (in bytes, not messages) */
size_t offset; /** Queue byte offset in the first message. Should be 0 unless a partial write. */
#ifdef SSL_SUPPORT
size_t last_send; /** When using SSL, one have to send the exact same buffer and length if a write cannot complete. */
#endif
struct linked_list* queue; /** List of queued messages */
};
struct hub_recvq
{
char* buf;
size_t size;
};
/**
* Create a send queue
*/
extern struct hub_sendq* hub_sendq_create();
/**
* Destroy a send queue, and delete any queued messages.
*/
extern void hub_sendq_destroy(struct hub_sendq*);
/**
* Add a message to the send queue.
*/
extern void hub_sendq_add(struct hub_sendq*, struct adc_message* msg);
/**
* Process the send queue, and send as many messages as possible.
* @returns the number of bytes sent.
* FIXME: send error not handled here!
*/
extern int hub_sendq_send(struct hub_sendq*, hub_recvq_write, void* data);
/**
* @returns 1 if send queue is empty, 0 otherwise.
*/
extern int hub_sendq_is_empty(struct hub_sendq*);
/**
* @returns the number of bytes remaining to be sent in the queue.
*/
extern size_t hub_sendq_get_bytes(struct hub_sendq*);
/**
* Create a receive queue.
*/
extern struct hub_recvq* hub_recvq_create();
/**
* Destroy a receive queue.
*/
extern void hub_recvq_destroy(struct hub_recvq*);
/**
* Gets the buffer, copies it into buf and deallocates it.
* NOTE: bufsize *MUST* be larger than the buffer, otherwise it asserts.
* @return the number of bytes copied into buf.
*/
extern size_t hub_recvq_get(struct hub_recvq*, void* buf, size_t bufsize);
/**
* Sets the buffer
*/
extern size_t hub_recvq_set(struct hub_recvq*, void* buf, size_t bufsize);
/**
* @return 1 if size is zero, 0 otherwise.
*/
extern int hub_recvq_is_empty(struct hub_recvq* buf);
#endif /* HAVE_UHUB_HUB_IO_H */

792
src/core/inf.c Normal file
View File

@@ -0,0 +1,792 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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"
/*
* These flags can only be set by the hub.
* Make sure we don't allow clients to specify these themselves.
*
* NOTE: Some of them are legacy ADC flags and no longer used, these
* should be removed at some point in the future when functionality no
* longer depend on them.
*/
static void remove_server_restricted_flags(struct adc_message* cmd)
{
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE); /* Client type flag (CT, obsoletes BO, RG, OP, HU) */
adc_msg_remove_named_argument(cmd, "BO"); /* Obsolete: bot flag (CT) */
adc_msg_remove_named_argument(cmd, "RG"); /* Obsolete: registered user flag (CT) */
adc_msg_remove_named_argument(cmd, "OP"); /* Obsolete: operator flag (CT) */
adc_msg_remove_named_argument(cmd, "HU"); /* Obsolete: hub flag (CT) */
adc_msg_remove_named_argument(cmd, "HI"); /* Obsolete: hidden user flag */
adc_msg_remove_named_argument(cmd, "TO"); /* Client to client token - should not be seen here */
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_REFERER);
}
static int set_feature_cast_supports(struct user* u, struct adc_message* cmd)
{
char *it, *tmp;
if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_SUPPORT))
{
tmp = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_SUPPORT);
user_clear_feature_cast_support(u);
it = tmp;
while (strlen(it) > 4)
{
it[4] = 0; /* FIXME: Not really needed */
user_set_feature_cast_support(u, it);
it = &it[5];
}
if (*it)
{
user_set_feature_cast_support(u, it);
}
hub_free(tmp);
}
return 0;
}
static int check_hash_tiger(const char* cid, const char* pid)
{
char x_pid[64];
char raw_pid[64];
uint64_t tiger_res[3];
memset(x_pid, 0, MAX_CID_LEN+1);
base32_decode(pid, (unsigned char*) raw_pid, MAX_CID_LEN);
tiger((uint64_t*) raw_pid, TIGERSIZE, (uint64_t*) tiger_res);
base32_encode((unsigned char*) tiger_res, TIGERSIZE, x_pid);
x_pid[MAX_CID_LEN] = 0;
if (strncasecmp(x_pid, cid, MAX_CID_LEN) == 0)
return 1;
return 0;
}
/*
* FIXME: Only works for tiger hash. If a client doesnt support tiger we cannot let it in!
*/
static int check_cid(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
size_t pos;
char* cid = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_CLIENT_ID);
char* pid = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID);
if (!cid || !pid)
{
hub_free(cid);
hub_free(pid);
return status_msg_error_no_memory;
}
if (strlen(cid) != MAX_CID_LEN)
{
hub_free(cid);
hub_free(pid);
return status_msg_inf_error_cid_invalid;
}
if (strlen(pid) != MAX_CID_LEN)
{
hub_free(cid);
hub_free(pid);
return status_msg_inf_error_pid_invalid;
}
for (pos = 0; pos < MAX_CID_LEN; pos++)
{
if (!is_valid_base32_char(cid[pos]))
{
hub_free(cid);
hub_free(pid);
return status_msg_inf_error_cid_invalid;
}
if (!is_valid_base32_char(pid[pos]))
{
hub_free(cid);
hub_free(pid);
return status_msg_inf_error_pid_invalid;
}
}
if (!check_hash_tiger(cid, pid))
{
hub_free(cid);
hub_free(pid);
return status_msg_inf_error_cid_invalid;
}
/* Set the cid in the user object */
memcpy(user->id.cid, cid, MAX_CID_LEN);
user->id.cid[MAX_CID_LEN] = 0;
hub_free(cid);
hub_free(pid);
return 0;
}
static int check_required_login_flags(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
int num = 0;
num = adc_msg_has_named_argument(cmd, ADC_INF_FLAG_CLIENT_ID);
if (num != 1)
{
if (!num)
return status_msg_inf_error_cid_missing;
return status_msg_inf_error_cid_invalid;
}
num = adc_msg_has_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID);
if (num != 1)
{
if (!num)
return status_msg_inf_error_pid_missing;
return status_msg_inf_error_pid_invalid;
}
num = adc_msg_has_named_argument(cmd, ADC_INF_FLAG_NICK);
if (num != 1)
{
if (!num)
return status_msg_inf_error_nick_missing;
return status_msg_inf_error_nick_multiple;
}
return 0;
}
/**
* This will check the ip address of the user, and
* remove any wrong address, and replace it with the correct one
* as seen by the hub.
*/
int check_network(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
const char* address = ip_convert_to_string(&user->net.ipaddr);
/* Check for NAT override address */
if (acl_is_ip_nat_override(hub->acl, address))
{
char* client_given_ip = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR);
if (strcmp(client_given_ip, "0.0.0.0") != 0)
{
user_set_nat_override(user);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_UDP_PORT);
hub_free(client_given_ip);
return 0;
}
hub_free(client_given_ip);
}
if (strchr(address, '.'))
{
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_UDP_PORT);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR);
adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR, address);
}
else if (strchr(address, ':'))
{
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_UDP_PORT);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR);
adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR, address);
}
return 0;
}
void strip_network(struct user* user, struct adc_message* cmd)
{
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_UDP_PORT);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_UDP_PORT);
}
static int nick_length_ok(const char* nick)
{
size_t length = strlen(nick);
if (length <= 1)
{
return nick_invalid_short;
}
if (length > MAX_NICK_LEN)
{
return nick_invalid_long;
}
return nick_ok;
}
static int nick_bad_characters(const char* nick)
{
const char* tmp;
/* Nick must not start with a space */
if (nick[0] == ' ')
return nick_invalid_spaces;
/* Check for ASCII values below 32 */
for (tmp = nick; *tmp; tmp++)
if ((*tmp < 32) && (*tmp > 0))
return nick_invalid_bad_ascii;
return nick_ok;
}
static int nick_is_utf8(const char* nick)
{
/*
* Nick should be valid utf-8, but
* perhaps we should check if the nick is unicode normalized?
*/
if (!is_valid_utf8(nick))
return nick_invalid_bad_utf8;
return nick_ok;
}
static int check_nick(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
char* nick;
char* tmp;
enum nick_status status;
tmp = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_NICK);
if (!tmp) return 0;
nick = adc_msg_unescape(tmp);
free(tmp); tmp = 0;
if (!nick) return 0;
status = nick_length_ok(nick);
if (status != nick_ok)
{
hub_free(nick);
if (status == nick_invalid_short)
return status_msg_inf_error_nick_short;
return status_msg_inf_error_nick_long;
}
status = nick_bad_characters(nick);
if (status != nick_ok)
{
hub_free(nick);
if (status == nick_invalid_spaces)
return status_msg_inf_error_nick_spaces;
return status_msg_inf_error_nick_bad_chars;
}
status = nick_is_utf8(nick);
if (status != nick_ok)
{
hub_free(nick);
return status_msg_inf_error_nick_not_utf8;
}
if (user_is_connecting(user))
{
memcpy(user->id.nick, nick, strlen(nick));
user->id.nick[strlen(nick)] = 0;
}
hub_free(nick);
return 0;
}
static int check_logged_in(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
struct user* lookup1 = uman_get_user_by_nick(hub, user->id.nick);
struct user* lookup2 = uman_get_user_by_cid(hub, user->id.cid);
if (lookup1 == user)
{
return 0;
}
if (lookup1 || lookup2)
{
if (lookup1 == lookup2)
{
hub_log(log_debug, "check_logged_in: exact same user is logged in: %s", user->id.nick);
hub_disconnect_user(hub, lookup1, quit_ghost_timeout);
return 0;
}
else
{
if (lookup1)
{
hub_log(log_debug, "check_logged_in: nickname is in use: %s", user->id.nick);
return status_msg_inf_error_nick_taken;
}
else
{
hub_log(log_debug, "check_logged_in: CID is in use: %s", user->id.cid);
return status_msg_inf_error_cid_taken;
}
}
}
return 0;
}
/*
* It is possible to do user-agent checking here.
* But this is not something we want to do, and is deprecated in the ADC specification.
* One should rather look at capabilities/features.
*/
static int check_user_agent(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
char* ua_encoded = 0;
char* ua = 0;
/* Get client user agent version */
ua_encoded = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_USER_AGENT);
if (ua_encoded)
{
ua = adc_msg_unescape(ua_encoded);
if (ua)
{
memcpy(user->user_agent, ua, MIN(strlen(ua), MAX_UA_LEN));
hub_free(ua);
}
}
hub_free(ua_encoded);
return 0;
}
static int check_acl(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
if (acl_is_cid_banned(hub->acl, user->id.cid))
{
return status_msg_ban_permanently;
}
if (acl_is_user_banned(hub->acl, user->id.nick))
{
return status_msg_ban_permanently;
}
if (acl_is_user_denied(hub->acl, user->id.nick))
{
return status_msg_inf_error_nick_restricted;
}
return 0;
}
static int check_limits(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
char* arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_SHARED_SIZE);
if (arg)
{
int64_t shared_size = atoll(arg);
if (shared_size < 0)
shared_size = 0;
if (user_is_logged_in(user))
{
hub->users->shared_size -= user->limits.shared_size;
hub->users->shared_size += shared_size;
}
user->limits.shared_size = shared_size;
hub_free(arg);
arg = 0;
}
arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_SHARED_FILES);
if (arg)
{
ssize_t shared_files = atoll(arg);
if (shared_files < 0)
shared_files = 0;
if (user_is_logged_in(user))
{
hub->users->shared_files -= user->limits.shared_files;
hub->users->shared_files += shared_files;
}
user->limits.shared_files = shared_files;
hub_free(arg);
arg = 0;
}
arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_NORMAL);
if (arg)
{
ssize_t num = atoll(arg);
if (num < 0) num = 0;
user->limits.hub_count_user = num;
hub_free(arg);
arg = 0;
}
arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_REGISTER);
if (arg)
{
ssize_t num = atoll(arg);
if (num < 0) num = 0;
user->limits.hub_count_registered = num;
hub_free(arg);
arg = 0;
}
arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_OPERATOR);
if (arg)
{
ssize_t num = atoll(arg);
if (num < 0) num = 0;
user->limits.hub_count_operator = num;
hub_free(arg);
arg = 0;
}
arg = adc_msg_get_named_argument(cmd, ADC_INF_FLAG_UPLOAD_SLOTS);
if (arg)
{
ssize_t num = atoll(arg);
if (num < 0) num = 0;
user->limits.upload_slots = num;
hub_free(arg);
arg = 0;
}
/* summarize total slots */
user->limits.hub_count_total = user->limits.hub_count_user + user->limits.hub_count_registered + user->limits.hub_count_operator;
if (!user_is_protected(user))
{
if (user->limits.shared_size < hub_get_min_share(hub) && hub_get_min_share(hub))
{
return status_msg_user_share_size_low;
}
if (user->limits.shared_size > hub_get_max_share(hub) && hub_get_max_share(hub))
{
return status_msg_user_share_size_high;
}
if ((user->limits.hub_count_user > hub_get_max_hubs_user(hub) && hub_get_max_hubs_user(hub)) ||
(user->limits.hub_count_registered > hub_get_max_hubs_reg(hub) && hub_get_max_hubs_reg(hub)) ||
(user->limits.hub_count_operator > hub_get_max_hubs_op(hub) && hub_get_max_hubs_op(hub)) ||
(user->limits.hub_count_total > hub_get_max_hubs_total(hub) && hub_get_max_hubs_total(hub)))
{
return status_msg_user_hub_limit_high;
}
if ((user->limits.hub_count_user < hub_get_min_hubs_user(hub) && hub_get_min_hubs_user(hub)) ||
(user->limits.hub_count_registered < hub_get_min_hubs_reg(hub) && hub_get_min_hubs_reg(hub)) ||
(user->limits.hub_count_operator < hub_get_min_hubs_op(hub) && hub_get_min_hubs_op(hub)))
{
return status_msg_user_hub_limit_low;
}
if (user->limits.upload_slots < hub_get_min_slots(hub) && hub_get_min_slots(hub))
{
return status_msg_user_slots_low;
}
if (user->limits.upload_slots > hub_get_max_slots(hub) && hub_get_max_slots(hub))
{
return status_msg_user_slots_high;
}
}
return 0;
}
/*
* Set the expected credentials, and returns 1 if authentication is needed,
* or 0 if not.
* If the hub is configured to allow only registered users and the user
* is not recognized this will return 1.
*/
static int set_credentials(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
int ret = 0;
struct user_access_info* info = acl_get_access_info(hub->acl, user->id.nick);
if (info)
{
user->credentials = info->status;
ret = 1;
}
else
{
user->credentials = cred_guest;
}
switch (user->credentials)
{
case cred_none:
break;
case cred_bot:
adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_BOT);
break;
case cred_guest:
/* Nothing to be added to the info message */
break;
case cred_user:
adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_REGISTERED_USER);
break;
case cred_operator:
adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_OPERATOR);
break;
case cred_super:
adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_SUPER_USER);
break;
case cred_admin:
adc_msg_add_argument(cmd, ADC_INF_FLAG_CLIENT_TYPE ADC_CLIENT_TYPE_ADMIN);
break;
case cred_link:
break;
}
return ret;
}
static int check_is_hub_full(struct hub_info* hub, struct user* user)
{
/*
* If hub is full, don't let users in, but we still want to allow
* operators and admins to enter the hub.
*/
if (hub->config->max_users && hub->users->count >= hub->config->max_users && !user_is_protected(user))
{
return 1;
}
return 0;
}
static int check_registered_users_only(struct hub_info* hub, struct user* user)
{
if (hub->config->registered_users_only && !user_is_registered(user))
{
return 1;
}
return 0;
}
static int hub_handle_info_common(struct user* user, struct adc_message* cmd)
{
/* Remove server restricted flags */
remove_server_restricted_flags(cmd);
/* Update/set the feature cast flags. */
set_feature_cast_supports(user, cmd);
return 0;
}
static int hub_handle_info_low_bandwidth(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
if (hub->config->low_bandwidth_mode)
{
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_USER_AGENT);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_SHARED_FILES);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_NORMAL);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_REGISTER);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_COUNT_HUB_OPERATOR);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_UPLOAD_SPEED);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_DOWNLOAD_SPEED);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_AUTO_SLOTS);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_AUTO_SLOTS_MAX);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_AWAY);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_DESCRIPTION);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_EMAIL);
}
return 0;
}
#define INF_CHECK(FUNC, HUB, USER, CMD) \
do { \
int ret = FUNC(HUB, USER, CMD); \
if (ret < 0) \
return ret; \
} while(0)
int hub_perform_login_checks(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
/* Make syntax checks. */
INF_CHECK(check_required_login_flags, hub, user, cmd);
INF_CHECK(check_cid, hub, user, cmd);
INF_CHECK(check_nick, hub, user, cmd);
INF_CHECK(check_network, hub, user, cmd);
INF_CHECK(check_user_agent, hub, user, cmd);
INF_CHECK(check_acl, hub, user, cmd);
INF_CHECK(check_logged_in, hub, user, cmd);
return 0;
}
/**
* Perform additional INF checks used at time of login.
*
* @return 0 if success, <0 if error, >0 if authentication needed.
*/
int hub_handle_info_login(struct hub_info* hub, struct user* user, struct adc_message* cmd)
{
int code = 0;
INF_CHECK(hub_perform_login_checks, hub, user, cmd);
/* Private ID must never be broadcasted - drop it! */
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID);
code = set_credentials(hub, user, cmd);
/* Note: this must be done *after* set_credentials. */
if (check_is_hub_full(hub, user))
{
return status_msg_hub_full;
}
if (check_registered_users_only(hub, user))
{
return status_msg_hub_registered_users_only;
}
INF_CHECK(check_limits, hub, user, cmd);
/* strip off stuff if low_bandwidth_mode is enabled */
hub_handle_info_low_bandwidth(hub, user, cmd);
/* Set initial user info */
user_set_info(user, cmd);
return code;
}
/*
* If user is in the connecting state, we need to do fairly
* strict checking of all arguments.
* This means we disconnect users when they provide invalid data
* during the login sequence.
* When users are merely updating their data after successful login
* we can just ignore any invalid data and not broadcast it.
*
* The data we need to check is:
* - nick name (valid, not taken, etc)
* - CID/PID (valid, not taken, etc).
* - IP addresses (IPv4 and IPv6)
*/
int hub_handle_info(struct hub_info* hub, struct user* user, const struct adc_message* cmd_unmodified)
{
struct adc_message* cmd = adc_msg_copy(cmd_unmodified);
if (!cmd) return -1; /* OOM */
cmd->priority = 1;
hub_handle_info_common(user, cmd);
/* If user is logging in, perform more checks,
otherwise only a few things need to be checked.
*/
if (user_is_connecting(user))
{
/*
* Don't allow the user to send multiple INF messages in this stage!
* Since that can have serious side-effects.
*/
if (user->info)
{
adc_msg_free(cmd);
return 0;
}
int ret = hub_handle_info_login(hub, user, cmd);
if (ret < 0)
{
on_login_failure(hub, user, ret);
adc_msg_free(cmd);
return -1;
}
else
{
/* Post a message, the user has joined */
struct event_data post;
memset(&post, 0, sizeof(post));
post.id = UHUB_EVENT_USER_JOIN;
post.ptr = user;
post.flags = ret; /* 0 - all OK, 1 - need authentication */
event_queue_post(hub->queue, &post);
adc_msg_free(cmd);
return 0;
}
}
else
{
/* These must not be allowed updated, let's remove them! */
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_CLIENT_ID);
/*
* If the nick is not accepted, do not relay it.
* Otherwise, the nickname will be updated.
*/
if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_NICK))
{
#if ALLOW_CHANGE_NICK
if (!check_nick(hub, user, cmd))
#endif
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_NICK);
}
/* FIXME - What if limits are not met ? */
check_limits(hub, user, cmd);
strip_network(user, cmd);
hub_handle_info_low_bandwidth(hub, user, cmd);
user_update_info(user, cmd);
if (!adc_msg_is_empty(cmd))
{
route_message(hub, user, cmd);
}
adc_msg_free(cmd);
}
return 0;
}

54
src/core/inf.h Normal file
View File

@@ -0,0 +1,54 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_INF_PARSER_H
#define HAVE_UHUB_INF_PARSER_H
enum nick_status
{
nick_ok = 0,
nick_invalid_short = -1,
nick_invalid_long = -2,
nick_invalid_spaces = -3,
nick_invalid_bad_ascii = -4,
nick_invalid_bad_utf8 = -5,
nick_invalid = -6, /* some unknown reason */
nick_not_allowed = -7, /* Not allowed according to configuration */
nick_banned = -8, /* Nickname is banned */
};
/**
* Handle info messages as received from clients.
* This can be an initial info message, which might end up requiring password
* authentication, etc.
* All sorts of validation is performed here.
* - Nickname valid?
* - CID/PID valid?
* - Network IP address valid?
*
* This can be triggered multiple times, as users can update their information,
* in such case nickname and CID/PID changes are not allowed.
*
* @return 0 on success, -1 on error
*/
extern int hub_handle_info(struct hub_info* hub, struct user* u, const struct adc_message* cmd);
#endif /* HAVE_UHUB_INF_PARSER_H */

479
src/core/main.c Normal file
View File

@@ -0,0 +1,479 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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"
static int arg_verbose = 5;
static int arg_fork = 0;
static int arg_check_config = 0;
static int arg_dump_config = 0;
static int arg_have_config = 0;
static const char* arg_uid = 0;
static const char* arg_gid = 0;
static const char* arg_config = 0;
static const char* arg_log = 0;
static const char* arg_pid = 0;
static int arg_log_syslog = 0;
#ifndef WIN32
void hub_handle_signal(int fd, short events, void* arg)
{
struct hub_info* hub = (struct hub_info*) arg;
int signal = fd;
switch (signal)
{
case SIGINT:
hub_log(log_info, "Interrupted. Shutting down...");
hub->status = hub_status_shutdown;
break;
case SIGTERM:
hub_log(log_info, "Terminated. Shutting down...");
hub->status = hub_status_shutdown;
break;
case SIGPIPE:
break;
case SIGHUP:
hub->status = hub_status_restart;
break;
default:
hub_log(log_trace, "hub_handle_signal(): caught unknown signal: %d", signal);
hub->status = hub_status_shutdown;
break;
}
}
static struct event signal_events[10];
static int signals[] =
{
SIGINT, /* Interrupt the application */
SIGTERM, /* Terminate the application */
SIGPIPE, /* prevent sigpipe from kills the application */
SIGHUP, /* reload configuration */
0
};
void setup_signal_handlers(struct hub_info* hub)
{
int i = 0;
for (i = 0; signals[i]; i++)
{
signal_set(&signal_events[i], signals[i], hub_handle_signal, hub);
event_base_set(hub->evbase, &signal_events[i]);
if (signal_add(&signal_events[i], NULL))
{
hub_log(log_error, "Error setting signal handler %d", signals[i]);
}
}
}
void shutdown_signal_handlers(struct hub_info* hub)
{
int i = 0;
for (i = 0; signals[i]; i++)
{
signal_del(&signal_events[i]);
}
}
#endif /* WIN32 */
int main_loop()
{
struct hub_config configuration;
struct acl_handle acl;
struct hub_info* hub = 0;
if (net_initialize() == -1)
return -1;
do
{
if (hub)
{
hub_log(log_info, "Reloading configuration files...");
hub_log(log_debug, "Hub status: %d", (int) hub->status);
}
if (read_config(arg_config, &configuration, !arg_have_config) == -1)
return -1;
if (acl_initialize(&configuration, &acl) == -1)
return -1;
/*
* Don't restart networking when re-reading configuration.
* This might not be possible either, since we might have
* dropped our privileges to do so.
*/
if (!hub)
{
hub = hub_start_service(&configuration);
if (!hub)
return -1;
#ifndef WIN32
setup_signal_handlers(hub);
#endif
}
hub_set_variables(hub, &acl);
hub_event_loop(hub);
hub_free_variables(hub);
acl_shutdown(&acl);
free_config(&configuration);
} while(hub->status != hub_status_shutdown);
#ifndef WIN32
shutdown_signal_handlers(hub);
#endif
if (hub)
{
hub_shutdown_service(hub);
}
net_destroy();
hub_log_shutdown();
return 0;
}
int check_configuration(int dump)
{
struct hub_config configuration;
int ret = read_config(arg_config, &configuration, 0);
if (dump)
{
if (ret != -1)
{
dump_config(&configuration, dump > 1);
}
return 0;
}
if (ret == -1)
{
fprintf(stderr, "ERROR\n");
return 1;
}
fprintf(stdout, "OK\n");
return 0;
}
void print_version()
{
fprintf(stdout, "" PRODUCT " " VERSION "\n");
fprintf(stdout, "Copyright (C) 2007-2009, Jan Vidar Krey <janvidar@extatic.org>\n"
"This is free software with ABSOLUTELY NO WARRANTY.\n\n");
exit(0);
}
void print_usage(char* program)
{
fprintf(stderr, "Usage: %s [options]\n\n", program);
fprintf(stderr,
"Options:\n"
" -v Verbose mode. Add more -v's for higher verbosity.\n"
" -q Quiet mode - no output\n"
" -f Fork to background\n"
" -l <file> Log messages to given file (default: stderr)\n"
" -L Log messages to syslog\n"
" -c <file> Specify configuration file (default: " SERVER_CONFIG ")\n"
" -C Check configuration and return\n"
" -s Show configuration parameters\n"
" -S Show configuration parameters, but ignore defaults\n"
" -h This message\n"
#ifndef WIN32
" -u <user> Run as given user\n"
" -g <group> Run with given group permissions\n"
" -p <file> Store pid in file (process id)\n"
#endif
" -V Show version number.\n"
);
exit(0);
}
void parse_command_line(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "vqfc:l:hu:g:VCsSLp:")) != -1)
{
switch (opt)
{
case 'V':
print_version();
break;
case 'v':
arg_verbose++;
break;
case 'q':
arg_verbose -= 99;
break;
case 'f':
arg_fork = 1;
break;
case 'c':
arg_config = optarg;
arg_have_config = 1;
break;
case 'C':
arg_check_config = 1;
arg_have_config = 1;
break;
case 's':
arg_dump_config = 1;
arg_check_config = 1;
break;
case 'S':
arg_dump_config = 2;
arg_check_config = 1;
break;
case 'l':
arg_log = optarg;
break;
case 'L':
arg_log_syslog = 1;
break;
case 'h':
print_usage(argv[0]);
break;
case 'u':
arg_uid = optarg;
break;
case 'g':
arg_gid = optarg;
break;
case 'p':
arg_pid = optarg;
break;
default:
print_usage(argv[0]);
break;
}
}
if (arg_config == NULL)
{
arg_config = SERVER_CONFIG;
}
hub_log_initialize(arg_log, arg_log_syslog);
hub_set_log_verbosity(arg_verbose);
}
#ifndef WIN32
int drop_privileges()
{
struct group* perm_group = 0;
struct passwd* perm_user = 0;
gid_t perm_gid = 0;
uid_t perm_uid = 0;
int gid_ok = 0;
int ret = 0;
if (arg_gid)
{
ret = 0;
while ((perm_group = getgrent()) != NULL)
{
if (strcmp(perm_group->gr_name, arg_gid) == 0)
{
perm_gid = perm_group->gr_gid;
ret = 1;
break;
}
}
endgrent();
if (!ret)
{
hub_log(log_fatal, "Unable to determine group id, check group name.");
return -1;
}
hub_log(log_trace, "Setting group id %d (%s)", (int) perm_gid, arg_gid);
ret = setgid(perm_gid);
if (ret == -1)
{
hub_log(log_fatal, "Unable to change group id, permission denied.");
return -1;
}
gid_ok = 1;
}
if (arg_uid)
{
ret = 0;
while ((perm_user = getpwent()) != NULL)
{
if (strcmp(perm_user->pw_name, arg_uid) == 0)
{
perm_uid = perm_user->pw_uid;
if (!gid_ok)
perm_gid = perm_user->pw_gid;
ret = 1;
break;
}
}
endpwent();
if (!ret)
{
hub_log(log_fatal, "Unable to determine user id, check user name.");
return -1;
}
if (!gid_ok) {
hub_log(log_trace, "Setting group id %d (%s)", (int) perm_gid, arg_gid);
ret = setgid(perm_gid);
if (ret == -1)
{
hub_log(log_fatal, "Unable to change group id, permission denied.");
return -1;
}
}
hub_log(log_trace, "Setting user id %d (%s)", (int) perm_uid, arg_uid);
ret = setuid(perm_uid);
if (ret == -1)
{
hub_log(log_fatal, "Unable to change user id, permission denied.");
return -1;
}
}
return 0;
}
int pidfile_create()
{
if (arg_pid)
{
FILE* pidfile = fopen(arg_pid, "w");
if (!pidfile)
{
hub_log(log_fatal, "Unable to write pid file: %s\n", arg_pid);
return -1;
}
fprintf(pidfile, "%d", (int) getpid());
fclose(pidfile);
}
return 0;
}
int pidfile_destroy()
{
if (arg_pid)
{
return unlink(arg_pid);
}
return 0;
}
#endif /* WIN32 */
int main(int argc, char** argv)
{
int ret = 0;
parse_command_line(argc, argv);
if (arg_check_config)
{
return check_configuration(arg_dump_config);
}
#ifndef WIN32
if (arg_fork)
{
ret = fork();
if (ret == -1)
{
hub_log(log_fatal, "Unable to fork to background!");
return -1;
}
else if (ret == 0)
{
/* child process - detatch from TTY */
fclose(stdin);
fclose(stdout);
fclose(stderr);
close(0);
close(1);
close(2);
}
else
{
/* parent process */
hub_log(log_debug, "Forked to background\n");
return 0;
}
}
if (pidfile_create() == -1)
return -1;
if (drop_privileges() == -1)
return -1;
#endif /* WIN32 */
ret = main_loop();
#ifndef WIN32
pidfile_destroy();
#endif
return ret;
}

350
src/core/netevent.c Normal file
View File

@@ -0,0 +1,350 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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 "hubio.h"
/* FIXME: This should not be needed! */
extern struct hub_info* g_hub;
#ifdef DEBUG_SENDQ
void debug_sendq_send(struct user* user, int sent, int total)
{
printf("SEND: sd=%d, %d/%d bytes\n", user->net.sd, sent, total);
if (sent == -1)
{
int err = net_error();
printf(" errno: %d - %s\n", err, net_error_string(err));
}
}
void debug_sendq_recv(struct user* user, int received, int max, const char* buffer)
{
printf("RECV: %d/%d bytes\n", received, (int) max);
if (received == -1)
{
int err = net_error();
printf(" errno: %d - %s\n", err, net_error_string(err));
}
else if (received > 0)
{
char* data = hub_malloc_zero(received + 1);
memcpy(data, buffer, received);
printf("RECV: \"%s\"\n", data);
hub_free(data);
}
}
#endif
int net_user_send(void* ptr, const void* buf, size_t len)
{
struct user* user = (struct user*) ptr;
int ret = net_send(user->net.sd, buf, len, UHUB_SEND_SIGNAL);
#ifdef DEBUG_SENDQ
debug_sendq_send(user, ret, len);
#endif
if (ret > 0)
{
user_reset_last_write(user);
}
else if (ret == -1 && (net_error() == EWOULDBLOCK || net_error() == EINTR))
{
return -2;
}
else
{
// user->close_flag = quit_socket_error;
return 0;
}
return ret;
}
#ifdef SSL_SUPPORT
int net_user_send_ssl(void* ptr, const void* buf, size_t len)
{
struct user* user = (struct user*) ptr;
int ret = SSL_write(user->net.ssl, buf, (int) len);
#ifdef DEBUG_SENDQ
debug_sendq_send(user, ret, len);
#endif
if (ret > 0)
{
user_reset_last_write(user);
}
else if (ret == -1 && net_error() == EWOULDBLOCK)
{
return -2;
}
else
{
// user->close_flag = quit_socket_error;
return 0;
}
return ret;
}
#endif
int net_user_recv(void* ptr, void* buf, size_t len)
{
struct user* user = (struct user*) ptr;
int ret = net_recv(user->net.sd, buf, len, 0);
if (ret > 0)
{
user_reset_last_read(user);
}
#ifdef DEBUG_SENDQ
debug_sendq_recv(user, ret, len, buf);
#endif
return ret;
}
#ifdef SSL_SUPPORT
int net_user_recv_ssl(void* ptr, void* buf, size_t len)
{
struct user* user = (struct user*) ptr;
int ret = SSL_read(user->net.ssl, buf, len);
if (ret > 0)
{
user_reset_last_read(user);
}
#ifdef DEBUG_SENDQ
debug_sendq_recv(user, ret, len, buf);
#endif
return ret;
}
#endif
int handle_net_read(struct user* user)
{
static char buf[MAX_RECV_BUF];
struct hub_recvq* q = user->net.recv_queue;
size_t buf_size = hub_recvq_get(q, buf, MAX_RECV_BUF);
ssize_t size = net_user_recv(user, &buf[buf_size], MAX_RECV_BUF - buf_size);
if (size > 0)
buf_size += size;
if (size == -1)
{
if (net_error() == EWOULDBLOCK || net_error() == EINTR)
return 0;
return quit_socket_error;
}
else if (size == 0)
{
return quit_disconnected;
}
else
{
char* lastPos = 0;
char* start = buf;
char* pos = 0;
size_t remaining = buf_size;
while ((pos = memchr(start, '\n', remaining)))
{
lastPos = pos;
pos[0] = '\0';
#ifdef DEBUG_SENDQ
printf("PROC: \"%s\" (%d)\n", start, (int) (pos - start));
#endif
if (user_flag_get(user, flag_maxbuf))
{
user_flag_unset(user, flag_maxbuf);
}
else
{
if (((pos - start) > 0) && g_hub->config->max_recv_buffer > (pos - start))
{
if (hub_handle_message(g_hub, user, start, (pos - start)) == -1)
{
return quit_protocol_error;
break;
}
}
}
pos[0] = '\n'; /* FIXME: not needed */
pos ++;
remaining -= (pos - start);
start = pos;
}
if (lastPos)
{
if (remaining < g_hub->config->max_recv_buffer)
{
hub_recvq_set(q, lastPos, remaining);
}
else
{
hub_recvq_set(q, 0, 0);
user_flag_set(user, flag_maxbuf);
}
}
else
{
hub_recvq_set(q, 0, 0);
}
}
return 0;
}
int handle_net_write(struct user* user)
{
while (hub_sendq_get_bytes(user->net.send_queue))
{
int ret = hub_sendq_send(user->net.send_queue, net_user_send, user);
if (ret == -2)
break;
if (ret <= 0)
return quit_socket_error;
}
if (hub_sendq_get_bytes(user->net.send_queue))
{
user_net_io_want_write(user);
}
else
{
user_net_io_want_read(user);
}
return 0;
}
void net_event(int fd, short ev, void *arg)
{
struct user* user = (struct user*) arg;
int flag_close = 0;
#ifdef DEBUG_SENDQ
hub_log(log_trace, "net_on_read() : fd=%d, ev=%d, arg=%p", fd, (int) ev, arg);
#endif
if (ev & EV_TIMEOUT)
{
if (user_is_connecting(user))
{
flag_close = quit_timeout;
}
else
{
// FIXME: hub is not neccesarily set!
// hub_send_ping(hub, user);
}
}
if (ev & EV_READ)
{
flag_close = handle_net_read(user);
}
else if (ev & EV_WRITE)
{
flag_close = handle_net_write(user);
}
if (flag_close)
{
hub_disconnect_user(g_hub, user, flag_close);
return;
}
}
static void prepare_user_net(struct hub_info* hub, struct user* user)
{
int fd = user->net.sd;
#ifdef SET_SENDBUG
size_t sendbuf = 0;
size_t recvbuf = 0;
if (net_get_recvbuf_size(fd, &recvbuf) != -1)
{
if (recvbuf > MAX_RECV_BUF || !recvbuf) recvbuf = MAX_RECV_BUF;
net_set_recvbuf_size(fd, recvbuf);
}
if (net_get_sendbuf_size(fd, &sendbuf) != -1)
{
if (sendbuf > MAX_SEND_BUF || !sendbuf) sendbuf = MAX_SEND_BUF;
net_set_sendbuf_size(fd, sendbuf);
}
#endif
net_set_nonblocking(fd, 1);
net_set_nosigpipe(fd, 1);
}
void net_on_accept(int server_fd, short ev, void *arg)
{
struct hub_info* hub = (struct hub_info*) arg;
struct user* user = 0;
struct ip_addr_encap ipaddr;
const char* addr;
for (;;)
{
int fd = net_accept(server_fd, &ipaddr);
if (fd == -1)
{
if (net_error() == EWOULDBLOCK)
{
break;
}
else
{
hub_log(log_error, "Accept error: %d %s", net_error(), strerror(net_error()));
break;
}
}
addr = ip_convert_to_string(&ipaddr);
/* FIXME: Should have a plugin log this */
hub_log(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))
{
hub_log(log_info, "Denied [%s] (IP banned)", addr);
net_close(fd);
continue;
}
user = user_create(hub, fd);
if (!user)
{
hub_log(log_error, "Unable to create user after socket accepted. Out of memory?");
net_close(fd);
break;
}
/* Store IP address in user object */
memcpy(&user->net.ipaddr, &ipaddr, sizeof(ipaddr));
prepare_user_net(hub, user);
}
}

35
src/core/netevent.h Normal file
View File

@@ -0,0 +1,35 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_NET_EVENT_H
#define HAVE_UHUB_NET_EVENT_H
/**
* Network callback to accept incoming connections.
*/
extern void net_on_accept(int fd, short ev, void *arg);
extern void net_event(int fd, short ev, void *arg);
extern int handle_net_read(struct user* user);
extern int handle_net_write(struct user* user);
#endif /* HAVE_UHUB_NET_EVENT_H */

219
src/core/route.c Normal file
View File

@@ -0,0 +1,219 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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"
int route_message(struct hub_info* hub, struct user* u, struct adc_message* msg)
{
struct user* target = NULL;
switch (msg->cache[0])
{
case 'B': /* Broadcast to all logged in clients */
route_to_all(hub, msg);
break;
case 'D':
target = uman_get_user_by_sid(hub, msg->target);
if (target)
{
route_to_user(hub, target, msg);
}
break;
case 'E':
target = uman_get_user_by_sid(hub, msg->target);
if (target)
{
route_to_user(hub, target, msg);
route_to_user(hub, u, msg);
}
break;
case 'F':
route_to_subscribers(hub, msg);
break;
default:
/* Ignore the message */
break;
}
return 0;
}
static inline size_t get_max_send_queue(struct hub_info* hub)
{
/* TODO: More dynamic send queue limit, for instance:
* return MAX(hub->config->max_send_buffer, (hub->config->max_recv_buffer * hub_get_user_count(hub)));
*/
return hub->config->max_send_buffer;
}
static inline size_t get_max_send_queue_soft(struct hub_info* hub)
{
return hub->config->max_send_buffer_soft;
}
/*
* @return 1 if send queue is OK.
* -1 if send queue is overflowed
* 0 if soft send queue is overflowed (not implemented at the moment)
*/
static inline int check_send_queue(struct hub_info* hub, struct user* user, struct adc_message* msg)
{
if (user_flag_get(user, flag_user_list))
return 1;
if ((user->net.send_queue->size + msg->length) > get_max_send_queue(hub))
return -1;
if (user->net.send_queue->size > get_max_send_queue_soft(hub) && msg->priority < 0)
return 0;
return 1;
}
int route_to_user(struct hub_info* hub, struct user* user, struct adc_message* msg)
{
#ifdef DEBUG_SENDQ
char* data = strndup(msg->cache, msg->length-1);
hub_log(log_protocol, "send %s: \"%s\"", sid_to_string(user->id.sid), data);
free(data);
#endif
if (hub_sendq_is_empty(user->net.send_queue) && !user_flag_get(user, flag_pipeline))
{
/* Perform oportunistic write */
hub_sendq_add(user->net.send_queue, msg);
handle_net_write(user);
}
else
{
if (check_send_queue(hub, user, msg) >= 0)
{
hub_sendq_add(user->net.send_queue, msg);
if (!user_flag_get(user, flag_pipeline))
user_net_io_want_write(user);
}
}
return 1;
}
int route_flush_pipeline(struct hub_info* hub, struct user* u)
{
if (hub_sendq_is_empty(u->net.send_queue))
return 0;
handle_net_write(u);
user_flag_unset(u, flag_pipeline);
return 1;
}
int route_to_all(struct hub_info* hub, struct adc_message* command) /* iterate users */
{
struct user* user = (struct user*) list_get_first(hub->users->list);
while (user)
{
route_to_user(hub, user, command);
user = (struct user*) list_get_next(hub->users->list);
}
return 0;
}
int route_to_subscribers(struct hub_info* hub, struct adc_message* command) /* iterate users */
{
int do_send;
char* tmp;
struct user* user = (struct user*) list_get_first(hub->users->list);
while (user)
{
if (user->feature_cast)
{
do_send = 1;
tmp = list_get_first(command->feature_cast_include);
while (tmp)
{
if (!user_have_feature_cast_support(user, tmp))
{
do_send = 0;
break;
}
tmp = list_get_next(command->feature_cast_include);;
}
if (!do_send) {
user = (struct user*) list_get_next(hub->users->list);
continue;
}
tmp = list_get_first(command->feature_cast_exclude);
while (tmp)
{
if (user_have_feature_cast_support(user, tmp))
{
do_send = 0;
break;
}
tmp = list_get_next(command->feature_cast_exclude);
}
if (do_send)
{
route_to_user(hub, user, command);
}
}
user = (struct user*) list_get_next(hub->users->list);
}
return 0;
}
int route_info_message(struct hub_info* hub, struct user* u)
{
if (!user_is_nat_override(u))
{
return route_to_all(hub, u->info);
}
else
{
struct adc_message* cmd = adc_msg_copy(u->info);
const char* address = ip_convert_to_string(&u->net.ipaddr);
struct user* user = 0;
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR);
adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR, address);
user = (struct user*) list_get_first(hub->users->list);
while (user)
{
if (user_is_nat_override(user))
route_to_user(hub, user, cmd);
else
route_to_user(hub, user, u->info);
user = (struct user*) list_get_next(hub->users->list);
}
adc_msg_free(cmd);
}
return 0;
}

56
src/core/route.h Normal file
View File

@@ -0,0 +1,56 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_ROUTE_H
#define HAVE_UHUB_ROUTE_H
/**
* Route a message by sending it to it's final destination.
*/
extern int route_message(struct hub_info* hub, struct user* u, struct adc_message* msg);
/**
* Send queued messages.
*/
extern int route_flush_pipeline(struct hub_info* hub, struct user* u);
/**
* Transmit message directly to one user.
*/
extern int route_to_user(struct hub_info* hub, struct user*, struct adc_message* command);
/**
* Broadcast message to all users.
*/
extern int route_to_all(struct hub_info* hub, struct adc_message* command);
/**
* Broadcast message to all users subscribing to the type of message.
*/
extern int route_to_subscribers(struct hub_info* hub, struct adc_message* command);
/**
* Broadcast initial info message to all users.
* This will ensure the correct IP is seen by other users
* in case nat override is in use.
*/
extern int route_info_message(struct hub_info* hub, struct user* user);
#endif /* HAVE_UHUB_ROUTE_H */

373
src/core/user.c Normal file
View File

@@ -0,0 +1,373 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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 DEBUG_SENDQ
static const char* user_log_str(struct user* user)
{
static char buf[128];
if (user)
{
snprintf(buf, 128, "user={ %p, \"%s\", %s/%s}", user, user->id.nick, sid_to_string(user->id.sid), user->id.cid);
}
else
{
snprintf(buf, 128, "user={ %p }", user);
}
return buf;
}
#endif
struct user* user_create(struct hub_info* hub, int sd)
{
struct user* user = NULL;
hub_log(log_trace, "user_create(), hub=%p, sd=%d", hub, sd);
user = (struct user*) hub_malloc_zero(sizeof(struct user));
if (user == NULL)
return NULL; /* OOM */
user->net.sd = sd;
user->net.tm_connected = time(NULL);
user->net.send_queue = hub_sendq_create();
user->net.recv_queue = hub_recvq_create();
event_set(&user->net.event, sd, EV_READ | EV_PERSIST, net_event, user);
event_base_set(hub->evbase, &user->net.event);
event_add(&user->net.event, 0);
evtimer_set(&user->net.timeout, net_event, user);
event_base_set(hub->evbase, &user->net.timeout);
user_set_timeout(user, TIMEOUT_CONNECTED);
user_set_state(user, state_protocol);
return user;
}
void user_destroy(struct user* user)
{
hub_log(log_trace, "user_destroy(), user=%p", user);
event_del(&user->net.event);
evtimer_del(&user->net.timeout);
hub_recvq_destroy(user->net.recv_queue);
hub_sendq_destroy(user->net.send_queue);
net_close(user->net.sd);
adc_msg_free(user->info);
user_clear_feature_cast_support(user);
hub_free(user);
}
void user_set_state(struct user* user, enum user_state state)
{
if ((user->state == state_cleanup && state != state_disconnected) || (user->state == state_disconnected))
{
return;
}
user->state = state;
}
void user_set_info(struct user* user, struct adc_message* cmd)
{
adc_msg_free(user->info);
user->info = adc_msg_incref(cmd);
}
void user_update_info(struct user* u, struct adc_message* cmd)
{
char prefix[2];
char* argument;
size_t n = 0;
struct adc_message* cmd_new = adc_msg_copy(u->info);
if (!cmd_new)
{
/* FIXME: OOM! */
return;
}
/*
* FIXME: Optimization potential:
*
* remove parts of cmd that do not really change anything in cmd_new.
* this can save bandwidth if clients send multiple updates for information
* that does not really change anything.
*/
argument = adc_msg_get_argument(cmd, n++);
while (argument)
{
if (strlen(argument) >= 2)
{
prefix[0] = argument[0];
prefix[1] = argument[1];
adc_msg_replace_named_argument(cmd_new, prefix, argument+2);
}
hub_free(argument);
argument = adc_msg_get_argument(cmd, n++);
}
user_set_info(u, cmd_new);
adc_msg_free(cmd_new);
}
static int convert_support_fourcc(int fourcc)
{
switch (fourcc)
{
case FOURCC('B','A','S','0'): /* Obsolete */
#ifndef OLD_ADC_SUPPORT
return 0;
#endif
case FOURCC('B','A','S','E'):
return feature_base;
case FOURCC('A','U','T','0'):
return feature_auto;
case FOURCC('U','C','M','0'):
case FOURCC('U','C','M','D'):
return feature_ucmd;
case FOURCC('Z','L','I','F'):
return feature_zlif;
case FOURCC('B','B','S','0'):
return feature_bbs;
case FOURCC('T','I','G','R'):
return feature_tiger;
case FOURCC('B','L','O','M'):
case FOURCC('B','L','O','0'):
return feature_bloom;
case FOURCC('P','I','N','G'):
return feature_ping;
case FOURCC('L','I','N','K'):
return feature_link;
default:
hub_log(log_debug, "Unknown extension: %x", fourcc);
return 0;
}
}
void user_support_add(struct user* user, int fourcc)
{
int feature_mask = convert_support_fourcc(fourcc);
user->flags |= feature_mask;
}
int user_flag_get(struct user* user, enum user_flags flag)
{
return user->flags & flag;
}
void user_flag_set(struct user* user, enum user_flags flag)
{
user->flags |= flag;
}
void user_flag_unset(struct user* user, enum user_flags flag)
{
user->flags &= ~flag;
}
void user_set_nat_override(struct user* user)
{
user_flag_set(user, flag_nat);
}
int user_is_nat_override(struct user* user)
{
return user_flag_get(user, flag_nat);
}
void user_support_remove(struct user* user, int fourcc)
{
int feature_mask = convert_support_fourcc(fourcc);
user->flags &= ~feature_mask;
}
void user_disconnect(struct user* user, int reason)
{
}
int user_have_feature_cast_support(struct user* user, char feature[4])
{
char* tmp = list_get_first(user->feature_cast);
while (tmp)
{
if (strncmp(tmp, feature, 4) == 0)
return 1;
tmp = list_get_next(user->feature_cast);
}
return 0;
}
int user_set_feature_cast_support(struct user* u, char feature[4])
{
if (!u->feature_cast)
{
u->feature_cast = list_create();
}
if (!u->feature_cast)
{
return 0; /* OOM! */
}
list_append(u->feature_cast, hub_strndup(feature, 4));
return 1;
}
void user_clear_feature_cast_support(struct user* u)
{
if (u->feature_cast)
{
list_clear(u->feature_cast, &hub_free);
list_destroy(u->feature_cast);
u->feature_cast = 0;
}
}
int user_is_logged_in(struct user* user)
{
if (user->state == state_normal)
return 1;
return 0;
}
int user_is_connecting(struct user* user)
{
if (user->state == state_protocol || user->state == state_identify || user->state == state_verify)
return 1;
return 0;
}
int user_is_protocol_negotiating(struct user* user)
{
if (user->state == state_protocol)
return 1;
return 0;
}
int user_is_disconnecting(struct user* user)
{
if (user->state == state_cleanup || user->state == state_disconnected)
return 1;
return 0;
}
int user_is_protected(struct user* user)
{
switch (user->credentials)
{
case cred_bot:
case cred_operator:
case cred_super:
case cred_admin:
case cred_link:
return 1;
default:
break;
}
return 0;
}
/**
* Returns 1 if a user is registered.
* Only registered users will be let in if the hub is configured for registered
* users only.
*/
int user_is_registered(struct user* user)
{
switch (user->credentials)
{
case cred_bot:
case cred_user:
case cred_operator:
case cred_super:
case cred_admin:
case cred_link:
return 1;
default:
break;
}
return 0;
}
void user_net_io_want_write(struct user* user)
{
#ifdef DEBUG_SENDQ
hub_log(log_trace, "user_net_io_want_write: %s (pending: %d)", user_log_str(user), event_pending(&user->net.event, EV_READ | EV_WRITE, 0));
#endif
if (event_pending(&user->net.event, EV_READ | EV_WRITE, 0) == (EV_READ | EV_WRITE))
return;
event_del(&user->net.event);
event_set(&user->net.event, user->net.sd, EV_READ | EV_WRITE | EV_PERSIST, net_event, user);
event_add(&user->net.event, 0);
}
void user_net_io_want_read(struct user* user)
{
#ifdef DEBUG_SENDQ
hub_log(log_trace, "user_net_io_want_read: %s (pending: %d)", user_log_str(user), event_pending(&user->net.event, EV_READ | EV_WRITE, 0));
#endif
if (event_pending(&user->net.event, EV_READ | EV_WRITE, 0) == EV_READ)
return;
event_del(&user->net.event);
event_set(&user->net.event, user->net.sd, EV_READ | EV_PERSIST, net_event, user);
event_add(&user->net.event, 0);
}
void user_reset_last_write(struct user* user)
{
user->net.tm_last_write = time(NULL);
}
void user_reset_last_read(struct user* user)
{
user->net.tm_last_read = time(NULL);
}
void user_set_timeout(struct user* user, int seconds)
{
#ifdef DEBUG_SENDQ
hub_log(log_trace, "user_set_timeout to %d seconds: %s", seconds, user_log_str(user));
#endif
struct timeval timeout = { seconds, 0 };
evtimer_add(&user->net.timeout, &timeout);
}

309
src/core/user.h Normal file
View File

@@ -0,0 +1,309 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_USER_H
#define HAVE_UHUB_USER_H
struct hub_info;
struct hub_iobuf;
enum user_state
{
state_protocol = 0, /**<< "User must send a valid protocol handshake" */
state_identify = 1, /**<< "User must send identification message (INF) " */
state_verify = 2, /**<< "User must send password to verify identification" */
state_normal = 3, /**<< "User is logged in." */
state_cleanup = 4, /**<< "User is disconnected, but other users need to be notified." */
state_disconnected = 5, /**<< "User is disconnected" */
};
enum user_flags
{
feature_base = 0x00000001, /** BASE: Basic configuration (required by all clients) */
feature_auto = 0x00000002, /** AUT0: Automatic nat detection traversal */
feature_bbs = 0x00000004, /** BBS0: Bulletin board system (not supported) */
feature_ucmd = 0x00000008, /** UCMD: User commands (not supported by this software) */
feature_zlif = 0x00000010, /** ZLIF: gzip stream compression (not supported) */
feature_tiger = 0x00000020, /** TIGR: Client supports the tiger hash algorithm */
feature_bloom = 0x00000040, /** BLO0: Bloom filter (not supported) */
feature_ping = 0x00000080, /** PING: Hub pinger information extension */
feature_link = 0x00000100, /** LINK: Hub link (not supported) */
flag_ignore = 0x01000000, /** Ignore further reads */
flag_maxbuf = 0x02000000, /** Hit max buf read, ignore msg */
flag_choke = 0x04000000, /** Choked: Cannot send, waiting for write event */
flag_want_read = 0x08000000, /** Need to read (SSL) */
flag_want_write = 0x10000000, /** Need to write (SSL) */
flag_user_list = 0x20000000, /** Send queue bypass (when receiving the send queue) */
flag_pipeline = 0x40000000, /** Hub message pipelining */
flag_nat = 0x80000000, /** nat override enabled */
};
enum user_quit_reason
{
quit_unknown = 0,
quit_disconnected = 1, /** User disconnected */
quit_kicked = 2, /** User was kicked */
quit_banned = 3, /** User was banned */
quit_timeout = 4, /** User timed out (no data for a while) */
quit_send_queue = 5, /** User's send queue was overflowed */
quit_memory_error = 6, /** Not enough memory available */
quit_socket_error = 7, /** A socket error occured */
quit_protocol_error = 8, /** Fatal protocol error */
quit_logon_error = 9, /** Unable to login (wrong password, CID/PID, etc) */
quit_hub_disabled = 10, /** Hub is disabled. No new connections allowed */
quit_ghost_timeout = 11, /** The user is a ghost, and trying to login from another connection */
};
struct user_info
{
sid_t sid; /** session ID */
char cid[MAX_CID_LEN+1]; /** global client ID */
char nick[MAX_NICK_LEN+1]; /** User's nick name */
};
/**
* This struct contains additional information about the user, such
* as the number of bytes and files shared, and the number of hubs the
* user is connected to, etc.
*/
struct user_limits
{
uint64_t shared_size; /** Shared size in bytes */
size_t shared_files; /** The number of shared files */
size_t upload_slots; /** The number of upload slots */
size_t hub_count_user; /** The number of hubs connected as user */
size_t hub_count_registered; /** The number of hubs connected as registered user */
size_t hub_count_operator; /** The number of hubs connected as operator */
size_t hub_count_total; /** The number of hubs connected to in total */
};
struct user_net_io
{
int sd; /** socket descriptor */
struct event event; /** libevent struct for read/write events */
struct event timeout; /** timeout handling */
struct hub_recvq* recv_queue;
struct hub_sendq* send_queue;
time_t tm_connected; /** time when user connected */
time_t tm_last_read; /** time the user last received something from the hub */
time_t tm_last_write; /** time the user last sent something to the hub */
struct ip_addr_encap ipaddr; /** IP address of connected user */
#ifdef SSL_SUPPORT
SSL* ssl; /** SSL handle */
#endif /* SSL_SUPPORT */
};
struct user
{
struct user_net_io net; /** Network information data */
enum user_state state; /** see enum user_state */
enum user_credentials credentials; /** see enum user_credentials */
struct user_info id; /** Contains nick name and CID */
int flags; /** see enum user_features */
char user_agent[MAX_UA_LEN+1];/** User agent string */
struct linked_list* feature_cast; /** Features supported by feature cast */
struct adc_message* info; /** ADC 'INF' message (broadcasted to everyone joining the hub) */
struct hub_info* hub; /** The hub instance this user belong to */
struct user_limits limits; /** Data used for limitation */
int quit_reason; /** Quit reason (see user_quit_reason) */
};
/**
* Create a user with the given socket descriptor.
* This basically only allocates memory and initializes all variables
* to an initial state.
*
* state is set to state_protocol.
*
* @param sd socket descriptor associated with the user
* @return User object or NULL if not enough memory is available.
*/
extern struct user* user_create(struct hub_info* hub, int sd);
/**
* Delete a user.
*
* !WRONG! If the user is logged in a quit message is issued.
*/
extern void user_destroy(struct user* user);
/**
* Disconnect a user.
* This will mark the user connection ready for being terminated.
* A reason can be given using the enum user_quit_reason.
*
* Things to be done when calling this:
* - Mark the user with state_cleanup
*
* If the user is logged in to the hub:
* - post message: UHUB_EVENT_USER_QUIT
*
* @param user User to disconnect
* @param reason See enum user_quit_reason
*/
extern void user_disconnect(struct user* user, int reason);
/**
* This associates a INF message to the user.
* If the user already has a INF message associated, then this is
* released before setting the new one.
*
* @param info new inf message (can be NULL)
*/
extern void user_set_info(struct user* user, struct adc_message* info);
/**
* Update a user's INF message.
* Will parse replace all ellements in the user's inf message with
* the parameters from the cmd (merge operation).
*/
extern void user_update_info(struct user* user, struct adc_message* cmd);
/**
* Specify a user's state.
* NOTE: DON'T, unless you know what you are doing.
*/
extern void user_set_state(struct user* user, enum user_state);
/**
* Returns 1 if the user is in state state_normal, or 0 otherwise.
*/
extern int user_is_logged_in(struct user* user);
/**
* Returns 1 if the user is in state_protocol.
* Returns 0 otherwise.
*/
extern int user_is_protocol_negotiating(struct user* user);
/**
* Returns 1 if the user is in state_protocol, state_identify or state_verify.
* Returns 0 otherwise.
*/
extern int user_is_connecting(struct user* user);
/**
* Returns 1 only if the user is in state_cleanup or state_disconnected.
*/
extern int user_is_disconnecting(struct user* user);
/**
* Returns 1 if a user is protected, which includes users
* having any form of elevated privileges.
*/
extern int user_is_protected(struct user* user);
/**
* Returns 1 if a user is registered, with or without privileges.
*/
extern int user_is_registered(struct user* user);
/**
* User supports the protocol extension as given in fourcc.
* This is usually set while the user is connecting, but can
* also be used to subscribe to a new class of messages from the
* hub.
*
* @see enum user_flags
*/
extern void user_support_add(struct user* user, int fourcc);
/**
* User no longer supports the protocol extension as given in fourcc.
* This can be used to unsubscribe to certain messages generated by
* the hub.
* @see enum user_flags
*/
extern void user_support_remove(struct user* user, int fourcc);
/**
* Sets the nat override flag for a user, this allows users on the same
* subnet as a natted hub to spoof their IP in order to use active mode
* on a natted hub.
*/
extern void user_set_nat_override(struct user* user);
extern int user_is_nat_override(struct user* user);
/**
* Set a flag. @see enum user_flags
*/
extern void user_flag_set(struct user* user, enum user_flags flag);
extern void user_flag_unset(struct user* user, enum user_flags flag);
/**
* Get a flag. @see enum user_flags
*/
extern int user_flag_get(struct user* user, enum user_flags flag);
/**
* Check if a user supports 'feature' for feature casting (basis for 'Fxxx' messages)
* The feature cast is specified as the 'SU' argument to the user's
* INF-message.
*
* @param feature a feature to lookup (example: 'TCP4' or 'UDP4')
* @return 1 if 'feature' supported, or 0 otherwise
*/
extern int user_have_feature_cast_support(struct user* user, char feature[4]);
/**
* Set feature cast support for feature.
*
* @param feature a feature to lookup (example: 'TCP4' or 'UDP4')
* @return 1 if 'feature' supported, or 0 otherwise
*/
extern int user_set_feature_cast_support(struct user* u, char feature[4]);
/**
* Remove all feature cast support features.
*/
extern void user_clear_feature_cast_support(struct user* u);
/**
* Mark the user with a want-write flag, meaning it should poll for writability.
*/
extern void user_net_io_want_write(struct user* user);
/**
* Mark the user with a want read flag, meaning it should poll for readability.
*/
extern void user_net_io_want_read(struct user* user);
/**
* Set timeout for connetion.
* @param seconds the number of seconds into the future.
*/
extern void user_set_timeout(struct user* user, int seconds);
/**
* Reset the last-write timer.
*/
extern void user_reset_last_write(struct user* user);
/**
* Reset the last-write timer.
*/
extern void user_reset_last_read(struct user* user);
#endif /* HAVE_UHUB_USER_H */

286
src/core/usermanager.c Normal file
View File

@@ -0,0 +1,286 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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"
#define USERMANAGER_TIMER
/*
* This callback function is used to clear user objects from the userlist.
* Should only be used in uman_shutdown().
*/
static void clear_user_list_callback(void* ptr)
{
if (ptr)
{
struct user* u = (struct user*) ptr;
/* Mark the user as already being disconnected.
* This prevents the hub from trying to send
* quit messages to other users.
*/
u->credentials = cred_none;
user_destroy(u);
}
}
void uman_update_stats(struct hub_info* hub)
{
const int factor = TIMEOUT_STATS;
struct net_statistics* total;
struct net_statistics* intermediate;
net_stats_get(&intermediate, &total);
hub->stats.net_tx = (intermediate->tx / factor);
hub->stats.net_rx = (intermediate->rx / factor);
hub->stats.net_tx_peak = MAX(hub->stats.net_tx, hub->stats.net_tx_peak);
hub->stats.net_rx_peak = MAX(hub->stats.net_rx, hub->stats.net_rx_peak);
hub->stats.net_tx_total = total->tx;
hub->stats.net_rx_total = total->rx;
net_stats_reset();
}
void uman_print_stats(struct hub_info* hub)
{
hub_log(log_info, "Statistics users=%zu (peak_users=%zu), net_tx=%d KB/s, net_rx=%d KB/s (peak_tx=%d KB/s, peak_rx=%d KB/s)",
hub->users->count,
hub->users->count_peak,
(int) hub->stats.net_tx / 1024,
(int) hub->stats.net_rx / 1024,
(int) hub->stats.net_tx_peak / 1024,
(int) hub->stats.net_rx_peak / 1024);
}
#ifdef USERMANAGER_TIMER
static void timer_statistics(int fd, short ev, void *arg)
{
struct hub_info* hub = (struct hub_info*) arg;
struct timeval timeout = { TIMEOUT_STATS, 0 };
uman_update_stats(hub);
evtimer_set(&hub->ev_timer, timer_statistics, hub);
event_base_set(hub->evbase, &hub->ev_timer);
evtimer_add(&hub->ev_timer, &timeout);
}
#endif
int uman_init(struct hub_info* hub)
{
struct user_manager* users = NULL;
#ifdef USERMANAGER_TIMER
struct timeval timeout = { TIMEOUT_STATS, 0 };
#endif
if (!hub)
return -1;
users = (struct user_manager*) hub_malloc_zero(sizeof(struct user_manager));
if (!users)
return -1;
users->list = list_create();
users->free_sid = 1;
if (!users->list)
{
list_destroy(users->list);
return -1;
}
hub->users = users;
#ifdef USERMANAGER_TIMER
if (hub->evbase)
{
evtimer_set(&hub->ev_timer, timer_statistics, hub);
event_base_set(hub->evbase, &hub->ev_timer);
evtimer_add(&hub->ev_timer, &timeout);
}
#endif // 0
return 0;
}
int uman_shutdown(struct hub_info* hub)
{
if (!hub || !hub->users)
return -1;
#ifdef USERMANAGER_TIMER
event_del(&hub->ev_timer);
#endif
if (hub->users->list)
{
list_clear(hub->users->list, &clear_user_list_callback);
list_destroy(hub->users->list);
}
hub_free(hub->users);
hub->users = 0;
return 0;
}
int uman_add(struct hub_info* hub, struct user* user)
{
if (!hub || !user)
return -1;
if (user->hub)
return -1;
list_append(hub->users->list, user);
hub->users->count++;
hub->users->count_peak = MAX(hub->users->count, hub->users->count_peak);
hub->users->shared_size += user->limits.shared_size;
hub->users->shared_files += user->limits.shared_files;
user->hub = hub;
return 0;
}
int uman_remove(struct hub_info* hub, struct user* user)
{
if (!hub || !user)
return -1;
list_remove(hub->users->list, user);
if (hub->users->count > 0)
{
hub->users->count--;
}
else
{
assert(!"negative count!");
}
hub->users->shared_size -= user->limits.shared_size;
hub->users->shared_files -= user->limits.shared_files;
user->hub = 0;
return 0;
}
struct user* uman_get_user_by_sid(struct hub_info* hub, sid_t sid)
{
struct user* user = (struct user*) list_get_first(hub->users->list); /* iterate users */
while (user)
{
if (user->id.sid == sid)
return user;
user = (struct user*) list_get_next(hub->users->list);
}
return NULL;
}
struct user* uman_get_user_by_cid(struct hub_info* hub, const char* cid)
{
struct user* user = (struct user*) list_get_first(hub->users->list); /* iterate users - only on incoming INF msg */
while (user)
{
if (strcmp(user->id.cid, cid) == 0)
return user;
user = (struct user*) list_get_next(hub->users->list);
}
return NULL;
}
struct user* uman_get_user_by_nick(struct hub_info* hub, const char* nick)
{
struct user* user = (struct user*) list_get_first(hub->users->list); /* iterate users - only on incoming INF msg */
while (user)
{
if (strcmp(user->id.nick, nick) == 0)
return user;
user = (struct user*) list_get_next(hub->users->list);
}
return NULL;
}
int uman_send_user_list(struct hub_info* hub, struct user* target)
{
int ret = 1;
user_flag_set(target, flag_user_list);
struct user* user = (struct user*) list_get_first(hub->users->list); /* iterate users - only on INF or PAS msg */
while (user)
{
if (user_is_logged_in(user))
{
ret = route_to_user(hub, target, user->info);
if (!ret)
break;
}
user = (struct user*) list_get_next(hub->users->list);
}
#if 0
FIXME: FIXME FIXME handle send queue excess
if (!target->send_queue_size)
{
user_flag_unset(target, flag_user_list);
}
#endif
return ret;
}
void uman_send_quit_message(struct hub_info* hub, struct user* leaving)
{
struct adc_message* command = adc_msg_construct(ADC_CMD_IQUI, 6);
adc_msg_add_argument(command, (const char*) sid_to_string(leaving->id.sid));
if (leaving->quit_reason == quit_banned || leaving->quit_reason == quit_kicked)
{
adc_msg_add_argument(command, ADC_QUI_FLAG_DISCONNECT);
}
route_to_all(hub, command);
adc_msg_free(command);
}
sid_t uman_get_free_sid(struct hub_info* hub)
{
#if 0
struct user* user;
user = (struct user*) list_get_first(hub->users->list); /* iterate normal users */
while (user)
{
if (user->sid == hub->users->free_sid)
{
hub->users->free_sid++;
if (hub->users->free_sid >= SID_MAX) hub->users->free_sid = 1;
break;
}
user = (struct user*) list_get_next(hub->users->list);
}
#endif
return hub->users->free_sid++;
}

117
src/core/usermanager.h Normal file
View File

@@ -0,0 +1,117 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007-2009, 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_USER_MANAGER_H
#define HAVE_UHUB_USER_MANAGER_H
struct user_manager
{
size_t count; /**<< "Number of all fully connected and logged in users" */
size_t count_peak; /**<< "Peak number of users" */
sid_t free_sid; /**<< "The next available SID." */
uint64_t shared_size; /**<< "The total number of shared bytes among fully connected users." */
uint64_t shared_files; /**<< "The total number of shared files among fully connected users." */
struct linked_list* list; /**<< "Contains all users" */
};
/**
* Initializes the user manager.
* @return 0 on success, or -1 if error (out of memory).
*/
extern int uman_init(struct hub_info* hub);
/**
* Shuts down the user manager.
* All users will be disconnected and deleted as part of this.
*
* @return 0 on success, or -1 in an error occured (hub is invalid).
*/
extern int uman_shutdown(struct hub_info* hub);
/**
* Generate statistics for logfiles.
*/
extern void uman_update_stats(struct hub_info* hub);
extern void uman_print_stats(struct hub_info* hub);
/**
* Add a user to the user manager.
*
* @param hub The hub to add the user to
* @param user The user to be added to the hub.
*/
extern int uman_add(struct hub_info* hub, struct user* user);
/**
* Remove a user from the user manager.
* This user is connected, and will be moved to the leaving queue, pending
* all messages in the message queue, and resource cleanup.
*
* @return 0 if successfully removed, -1 if error.
*/
extern int uman_remove(struct hub_info* hub, struct user* user);
/**
* Returns and allocates an unused session ID (SID).
*/
extern sid_t uman_get_free_sid(struct hub_info* hub);
/**
* Lookup a user based on the session ID (SID).
*
* NOTE: This function will only search connected users, which means
* that SIDs assigned to users who are not yet completely logged in,
* or are in the process of being disconnected will result in this
* function returning NULL even though the sid is not freely available.
*
* FIXME: Is that really safe / sensible ?
* - Makes sense from a message routing point of view.
*
* @return a user if found, or NULL if not found
*/
extern struct user* uman_get_user_by_sid(struct hub_info* hub, sid_t sid);
/**
* Lookup a user based on the client ID (CID).
* @return a user if found, or NULL if not found
*/
extern struct user* uman_get_user_by_cid(struct hub_info* hub, const char* cid);
/**
* Lookup a user based on the nick name.
* @return a user if found, or NULL if not found
*/
extern struct user* uman_get_user_by_nick(struct hub_info* hub, const char* nick);
/**
* Send the user list of connected clients to 'user'.
* Usually part of the login process.
*
* @return 1 if sending the user list succeeded, 0 otherwise.
*/
extern int uman_send_user_list(struct hub_info* hub, struct user* user);
/**
* Send a quit message to all connected users when 'user' is
* leaving the hub (for whatever reason).
*/
extern void uman_send_quit_message(struct hub_info* hub, struct user* user);
#endif /* HAVE_UHUB_USER_MANAGER_H */