First git commit.

This commit is contained in:
Jan Vidar Krey
2009-02-19 17:14:09 +01:00
commit ac56e7a5b9
72 changed files with 16652 additions and 0 deletions

142
src/adcconst.h Normal file
View File

@@ -0,0 +1,142 @@
/*
* 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_ADC_CONSTANTS_H
#define HAVE_UHUB_ADC_CONSTANTS_H
typedef uint32_t sid_t;
typedef uint32_t fourcc_t;
/* Internal uhub limit */
#define MAX_ADC_CMD_LEN 4096
#define FOURCC(a,b,c,d) (fourcc_t) ((a << 24) | (b << 16) | (c << 8) | d)
/* default welcome protocol support message, as sent by this server */
#define ADC_PROTO_SUPPORT "ADBASE ADTIGR ADPING"
/* Server sent commands */
#define ADC_CMD_ISID FOURCC('I','S','I','D')
#define ADC_CMD_ISUP FOURCC('I','S','U','P')
#define ADC_CMD_IGPA FOURCC('I','G','P','A')
#define ADC_CMD_ISTA FOURCC('I','S','T','A')
#define ADC_CMD_IINF FOURCC('I','I','N','F')
#define ADC_CMD_IMSG FOURCC('I','M','S','G')
#define ADC_CMD_IQUI FOURCC('I','Q','U','I')
/* Handshake and login/passwordstuff */
#define ADC_CMD_HSUP FOURCC('H','S','U','P')
#define ADC_CMD_HPAS FOURCC('H','P','A','S')
#define ADC_CMD_HINF FOURCC('H','I','N','F')
#define ADC_CMD_BINF FOURCC('B','I','N','F')
/* This is a Admin extension */
#define ADC_CMD_HDSC FOURCC('H','D','S','C')
/* Status/error messages */
#define ADC_CMD_HSTA FOURCC('H','S','T','A')
#define ADC_CMD_DSTA FOURCC('D','S','T','A')
/* searches */
#define ADC_CMD_BSCH FOURCC('B','S','C','H')
#define ADC_CMD_DSCH FOURCC('D','S','C','H')
#define ADC_CMD_ESCH FOURCC('E','S','C','H')
#define ADC_CMD_FSCH FOURCC('F','S','C','H')
#define ADC_CMD_DRES FOURCC('D','R','E','S')
/* connection setup */
#define ADC_CMD_DCTM FOURCC('D','C','T','M')
#define ADC_CMD_DRCM FOURCC('D','R','C','M')
#define ADC_CMD_ECTM FOURCC('E','C','T','M')
#define ADC_CMD_ERCM FOURCC('E','R','C','M')
/* chat messages */
#define ADC_CMD_BMSG FOURCC('B','M','S','G')
#define ADC_CMD_DMSG FOURCC('D','M','S','G')
#define ADC_CMD_EMSG FOURCC('E','M','S','G')
#define ADC_CMD_FMSG FOURCC('F','M','S','G')
/* disallowed messages */
#define ADC_CMD_DINF FOURCC('D','I','N','F')
#define ADC_CMD_EINF FOURCC('E','I','N','F')
#define ADC_CMD_FINF FOURCC('F','I','N','F')
/* Extension messages */
#define ADC_CMD_HCHK FOURCC('H','C','H','K')
#define ADC_INF_FLAG_IPV4_ADDR "I4" /* ipv4 address */
#define ADC_INF_FLAG_IPV6_ADDR "I6" /* ipv6 address */
#define ADC_INF_FLAG_IPV4_UDP_PORT "U4" /* port number */
#define ADC_INF_FLAG_IPV6_UDP_PORT "U6" /* port number */
#define ADC_INF_FLAG_CLIENT_TYPE "CT" /* client type */
#define ADC_INF_FLAG_PRIVATE_ID "PD" /* private id, aka PID */
#define ADC_INF_FLAG_CLIENT_ID "ID" /* client id, aka CID */
#define ADC_INF_FLAG_NICK "NI" /* nick name */
#define ADC_INF_FLAG_DESCRIPTION "DE" /* user description */
#define ADC_INF_FLAG_USER_AGENT "VE" /* software version */
#define ADC_INF_FLAG_SUPPORT "SU" /* support (extensions, feature cast) */
#define ADC_INF_FLAG_SHARED_SIZE "SS" /* size of total files shared in bytes */
#define ADC_INF_FLAG_SHARED_FILES "SF" /* number of files shared */
#define ADC_INF_FLAG_UPLOAD_SPEED "US" /* maximum upload speed acheived in bytes/sec */
#define ADC_INF_FLAG_DOWNLOAD_SPEED "DS" /* maximum download speed acheived in bytes/sec */
#define ADC_INF_FLAG_UPLOAD_SLOTS "SL" /* maximum upload slots (concurrent uploads) */
#define ADC_INF_FLAG_AUTO_SLOTS "AS" /* automatic slot if upload speed is less than this in bytes/sec */
#define ADC_INF_FLAG_AUTO_SLOTS_MAX "AM" /* maximum number of automatic slots */
#define ADC_INF_FLAG_COUNT_HUB_NORMAL "HN" /* user is logged into this amount of hubs as a normal user (guest) */
#define ADC_INF_FLAG_COUNT_HUB_REGISTER "HR" /* user is logged into this amount of hubs as a registered user (password) */
#define ADC_INF_FLAG_COUNT_HUB_OPERATOR "HO" /* user is logged into this amount of hubs as an operator */
#define ADC_INF_FLAG_AWAY "AW" /* away flag, 1=away, 2=extended away */
#define ADC_INF_FLAG_REFERER "RF" /* URL to referer in case of hub redirect */
#define ADC_INF_FLAG_EMAIL "EM" /* E-mail address */
#define ADC_MSG_FLAG_ACTION "ME" /* message is an *action* message */
#define ADC_MSG_FLAG_PRIVATE "PM" /* message is a private message */
#define ADC_SCH_FLAG_INCLUDE "AN" /* include given search term */
#define ADC_SCH_FLAG_EXCLUDE "NO" /* exclude given serach term */
#define ADC_SCH_FLAG_FILE_EXTENSION "EX" /* search only for files with the given file extension */
#define ADC_SCH_FLAG_FILE_TYPE "TY" /* search only for files with this file type (separate type) */
#define ADC_SCH_FLAG_LESS_THAN "LE" /* search for files with this size or less */
#define ADC_SCH_FLAG_GREATER_THAN "GE" /* search for files with this size or greater */
#define ADC_SCH_FLAG_EQUAL "EQ" /* search only for files with this exact size */
#define ADC_SCH_FLAG_TOKEN "TO" /* use this token for search replies */
#define ADC_RES_FLAG_FILE_NAME "FN" /* file name */
#define ADC_RES_FLAG_FILE_SIZE "SI" /* file size */
#define ADC_RES_FLAG_UPLOAD_SLOTS "SL" /* number of upload slots available (if > 0, download is possible) */
#define ADC_RES_FLAG_TOKEN "TO" /* token, same as the token in the search request */
#define ADC_QUI_FLAG_TIME_LEFT "TL" /* time in seconds before reconnect is allowed, or -1 forever */
#define ADC_QUI_FLAG_MESSAGE "MS" /* kick/leave message */
#define ADC_QUI_FLAG_DISCONNECT "DI" /* all further transfers with this user should be disconnected */
#define ADC_QUI_FLAG_REDIRECT "RD" /* redirect to URL */
#define ADC_QUI_FLAG_KICK_OPERATOR "ID" /* SID of operator who disconnected the user */
#define ADC_SUP_FLAG_ADD "AD"
#define ADC_SUP_FLAG_REMOVE "RM"
#define ADC_CLIENT_TYPE_BOT "1"
#define ADC_CLIENT_TYPE_REGISTERED_USER "2"
#define ADC_CLIENT_TYPE_OPERATOR "4"
#define ADC_CLIENT_TYPE_SUPER_USER "8"
#define ADC_CLIENT_TYPE_ADMIN "16" /* hub owner */
#define ADC_CLIENT_TYPE_HUB "32" /* the hub itself */
#endif /* HAVE_UHUB_ADC_CONSTANTS_H */

950
src/adcrush.c Normal file
View File

@@ -0,0 +1,950 @@
/**
* An ADC client emulator.
*/
#include "uhub.h"
#define ADC_CLIENTS_DEFAULT 100
#define ADC_MAX_CLIENTS 25000
#define ADC_BUFSIZE 16384
#define ADC_SIDSIZE 4
#define ADC_CID_SIZE 39
#define BIG_BUFSIZE 131072
#define TIGERSIZE 24
#define ADC_HANDSHAKE "HSUP ADBASE ADTIGR\n"
#define ADCRUSH "adcrush/0.2"
#define ADC_NICK "[BOT]adcrush"
#define ADC_DESC "crash\\stest\\sdummy"
struct ADC_client;
static void ADC_client_on_disconnected(struct ADC_client*);
static void ADC_client_on_connected(struct ADC_client*);
static void ADC_client_on_login(struct ADC_client*);
static void ADC_client_connect(struct ADC_client*);
static void ADC_client_disconnect(struct ADC_client*);
static int ADC_client_create(struct ADC_client* client, int num);
static void ADC_client_destroy(struct ADC_client* client);
static int cfg_mode = 0; // See enum operationMode
static char* cfg_host = 0;
static int cfg_port = 0;
static int cfg_debug = 0; /* debug level */
static int cfg_level = 1; /* activity level (0..3) */
static int cfg_chat = 0; /* chat mode, allow sending chat messages */
static int cfg_quiet = 0; /* quiet mode (no output) */
static int cfg_clients = ADC_CLIENTS_DEFAULT; /* number of clients */
static int running = 1;
static struct sockaddr_in saddr;
enum commandMode
{
cm_bcast = 0x01, /* B - broadcast */
cm_dir = 0x02, /* D - direct message */
cm_echo = 0x04, /* E - echo message */
cm_fcast = 0x08, /* F - feature cast message */
cm_c2h = 0x10, /* H - client to hub message */
cm_h2c = 0x20, /* I - hub to client message */
cm_c2c = 0x40, /* C - client to client message */
cm_udp = 0x80, /* U - udp message (client to client) */
};
enum commandValidity
{
cv_protocol = 0x01,
cv_identify = 0x02,
cv_verify = 0x04,
cv_normal = 0x08,
};
enum protocolState
{
ps_none = 0x00, /* none or disconnected */
ps_conn = 0x01, /* connecting... */
ps_protocol = 0x02,
ps_identify = 0x04,
ps_verify = 0x08,
ps_normal = 0x10,
};
enum operationMode
{
mode_performance = 0x01,
mode_bugs = 0x02,
mode_security = 0x04,
mode_log = 0x08,
};
struct commandPattern
{
unsigned char mode; /* see enum commandMode */
char cmd[3];
unsigned char validity; /* see enum commandValidity */
};
const struct commandPattern patterns[] =
{
{ cm_c2h | cm_c2c | cm_h2c, "SUP", cv_protocol | cv_normal }, /* protocol support */
{ cm_bcast | cm_h2c | cm_c2c, "INF", cv_identify | cv_verify | cv_normal }, /* info message */
{ cm_bcast | cm_h2c | cm_c2c | cm_c2h | cm_udp, "STA", cv_protocol | cv_identify | cv_verify | cv_normal }, /* status message */
{ cm_bcast | cm_dir | cm_echo | cm_h2c, "MSG", cv_normal }, /* chat message */
{ cm_bcast | cm_dir | cm_echo | cm_fcast, "SCH", cv_normal }, /* search */
{ cm_dir | cm_udp, "RES", cv_normal }, /* search result */
{ cm_dir | cm_echo, "CTM", cv_normal }, /* connect to me */
{ cm_dir | cm_echo, "RCM", cv_normal }, /* reversed, connect to me */
{ cm_h2c, "QUI", cv_normal }, /* quit message */
{ cm_h2c, "GPA", cv_identify }, /* password request */
{ cm_c2h, "PAS", cv_verify } /* password response */
};
#define MAX_CHAT_MSGS 35
const char* chat_messages[MAX_CHAT_MSGS] = {
"hello",
"I'm an annoying robot, configured to chat in order to measure performance of the hub.",
"I apologize for the inconvenience.",
".",
":)",
"can anyone help me, pls?",
"wtf?",
"bullshit",
"resistance is futile.",
"You crossed the line first, sir. You squeezed them, you hammered them to the point of desperation. And in their desperation they turned to a man they didn't fully understand.",
"beam me up, scotty",
"morning",
"You know where Harvey is? You know who he is?",
"gtg",
"thanks",
"*punt*",
"*nudge*",
"that's ok",
"...anyway",
"hola",
"hey",
"hi",
"nevermind",
"i think so",
"dunno",
"debian ftw",
"oops",
"how do I search?",
"how do I enable active mode?",
"home, sweet home...",
"later",
"Good evening, ladies and gentlemen. We are tonight's entertainment! I only have one question. Where is Harvey Dent?",
"You know where I can find Harvey? I need to talk to him about something. Just something, a little.",
"We really should stop fighting, we'll miss the fireworks!",
"Wanna know how I got these scars?",
};
#define MAX_SEARCH_MSGS 10
const char* search_messages[MAX_SEARCH_MSGS] = {
"ANmp3 TOauto",
"ANxxx TOauto",
"ANdivx TOauto",
"ANtest ANfoo TOauto",
"ANwmv TO1289718",
"ANbabe TO8981884",
"ANpr0n TOauto",
"ANmusic TOauto",
"ANvideo TOauto",
"ANburnout ANps3 TOauto",
};
struct ADC_client
{
int sd;
int num;
sid_t sid;
enum protocolState state;
char info[ADC_BUFSIZE];
char recvbuf[BIG_BUFSIZE];
char sendbuf[BIG_BUFSIZE];
size_t s_offset;
size_t r_offset;
struct event ev_read;
struct event ev_write;
struct event ev_timer;
size_t timeout;
};
static void bot_output(struct ADC_client* client, const char* format, ...)
{
char logmsg[1024];
va_list args;
va_start(args, format);
vsnprintf(logmsg, 1024, format, args);
va_end(args);
if (cfg_mode == mode_log)
{
fprintf(stdout, "%s\n", logmsg);
}
else
{
if (cfg_debug)
fprintf(stdout, "* [%4d] %s\n", client->num, logmsg);
}
}
static void adc_cid_pid(struct ADC_client* client)
{
char seed[64];
char pid[64];
char cid[64];
uint64_t tiger_res1[3];
uint64_t tiger_res2[3];
/* create cid+pid pair */
memset(seed, 0, 64);
snprintf(seed, 64, ADCRUSH "%p/%d", client, (int) client->num);
tiger((uint64_t*) seed, strlen(seed), tiger_res1);
base32_encode((unsigned char*) tiger_res1, TIGERSIZE, pid);
tiger((uint64_t*) tiger_res1, TIGERSIZE, tiger_res2);
base32_encode((unsigned char*) tiger_res2, TIGERSIZE, cid);
cid[ADC_CID_SIZE] = 0;
pid[ADC_CID_SIZE] = 0;
strcat(client->info, " PD");
strcat(client->info, pid);
strcat(client->info, " ID");
strcat(client->info, cid);
}
static size_t get_wait_rand(size_t max)
{
static size_t next = 0;
if (next == 0) next = (size_t) time(0);
next = (next * 1103515245) + 12345;
return ((size_t )(next / 65536) % max);
}
static void client_reschedule_timeout(struct ADC_client* client)
{
size_t next_timeout = 0;
struct timeval timeout = { 0, 0 };
switch (client->state)
{
case ps_conn: next_timeout = 30; break;
case ps_protocol: next_timeout = 30; break;
case ps_identify: next_timeout = 30; break;
case ps_verify: next_timeout = 30; break;
case ps_normal: next_timeout = 120; break;
case ps_none: next_timeout = 120; break;
}
if (client->state == ps_normal || client->state == ps_none)
{
switch (cfg_level)
{
case 0: /* polite */
next_timeout *= 4;
break;
case 1: /* normal */
break;
case 2: /* aggressive */
next_timeout /= 8;
break;
case 3: /* excessive */
next_timeout /= 16;
case 4: /* excessive */
next_timeout /= 32;
}
}
if (client->state == ps_conn)
client->timeout = MAX(next_timeout, 1);
else
client->timeout = get_wait_rand(MAX(next_timeout, 1));
if (!client->timeout) client->timeout++;
timeout.tv_sec = (time_t) client->timeout;
evtimer_add(&client->ev_timer, &timeout);
}
static void set_state_timeout(struct ADC_client* client, enum protocolState state)
{
client->state = state;
client_reschedule_timeout(client);
}
static void send_client(struct ADC_client* client, char* msg)
{
int ret = net_send(client->sd, msg, strlen(msg), UHUB_SEND_SIGNAL);
if (cfg_debug > 1)
{
char* dump = strdup(msg);
dump[strlen(msg) - 1] = 0;
bot_output(client, "- SEND: '%s'", dump);
free(dump);
}
if (ret != strlen(msg))
{
if (ret == -1)
{
if (net_error() != EWOULDBLOCK)
ADC_client_on_disconnected(client);
}
else
{
/* FIXME: Not all data sent! */
printf("ret (%d) != msg->length (%d)\n", ret, (int) strlen(msg));
}
}
}
static void ADC_client_on_connected(struct ADC_client* client)
{
send_client(client, ADC_HANDSHAKE);
set_state_timeout(client, ps_protocol);
bot_output(client, "connected.");
}
static void ADC_client_on_disconnected(struct ADC_client* client)
{
event_del(&client->ev_read);
event_del(&client->ev_write);
net_close(client->sd);
client->sd = -1;
bot_output(client, "disconnected.");
set_state_timeout(client, ps_none);
}
static void ADC_client_on_login(struct ADC_client* client)
{
bot_output(client, "logged in.");
set_state_timeout(client, ps_normal);
}
static void send_client_info(struct ADC_client* client)
{
client->info[0] = 0;
strcat(client->info, "BINF ");
strcat(client->info, sid_to_string(client->sid));
strcat(client->info, " NI" ADC_NICK);
if (cfg_clients > 1)
{
strcat(client->info, "_");
strcat(client->info, uhub_itoa(client->num));
}
strcat(client->info, " VE" ADCRUSH);
strcat(client->info, " DE" ADC_DESC);
strcat(client->info, " I40.0.0.0");
strcat(client->info, " EMuhub@extatic.org");
strcat(client->info, " SL3");
strcat(client->info, " HN1");
strcat(client->info, " HR1");
strcat(client->info, " HO1");
adc_cid_pid(client);
strcat(client->info, "\n");
send_client(client, client->info);
}
static void perf_result(struct ADC_client* client, sid_t target, const char* what, const char* token);
static int recv_client(struct ADC_client* client)
{
ssize_t size = 0;
if (cfg_mode != mode_performance || (cfg_mode == mode_performance && (get_wait_rand(100) < (90 - (15 * cfg_level)))))
{
size = net_recv(client->sd, &client->recvbuf[client->r_offset], ADC_BUFSIZE - client->r_offset, 0);
}
else
{
if (get_wait_rand(1000) == 99)
return -1; /* Can break tings badly! :-) */
else
return 0;
}
if (size == 0 || ((size == -1 && net_error() != EWOULDBLOCK)))
return -1;
client->recvbuf[client->r_offset + size] = 0;
char* start = client->recvbuf;
char* pos;
char* lastPos;
while ((pos = strchr(start, '\n')))
{
lastPos = pos;
pos[0] = 0;
if (cfg_debug > 1)
{
bot_output(client, "- RECV: '%s'", start);
}
fourcc_t cmd = 0;
if (strlen(start) < 4)
{
bot_output(client, "Unexpected response from hub: '%s'", start);
start = &pos[1];
continue;
}
cmd = FOURCC(start[0], start[1], start[2], start[3]);
switch (cmd)
{
case ADC_CMD_ISUP:
break;
case ADC_CMD_ISID:
if (client->state == ps_protocol)
{
client->sid = string_to_sid(&start[5]);
client->state = ps_identify;
send_client_info(client);
}
break;
case ADC_CMD_IINF:
break;
case ADC_CMD_BSCH:
case ADC_CMD_FSCH:
{
if (get_wait_rand(100) > (90 - (10 * cfg_level)) && cfg_mode == mode_performance)
{
sid_t target = string_to_sid(&start[5]);
const char* what = strstr(&start[5], " AN");
const char* token = strstr(&start[5], " TO");
char* split = 0;
if (!token || !what) break;
token += 3;
what += 3;
split = strchr(what, ' ');
if (!split) break;
else split[0] = '0';
split = strchr(token, ' ');
if (split) split[0] = '0';
perf_result(client, target, what, token);
}
break;
}
case ADC_CMD_BINF:
{
if (strlen(start) > 9)
{
char t = start[9]; start[9] = 0; sid_t sid = string_to_sid(&start[5]); start[9] = t;
if (sid == client->sid)
{
if (client->state == ps_verify || client->state == ps_identify)
{
ADC_client_on_login(client);
}
}
}
break;
}
case ADC_CMD_ISTA:
if (strncmp(start, "ISTA 000", 8))
{
bot_output(client, "status: '%s'\n", (start + 9));
}
break;
default:
break;
}
start = &pos[1];
}
client->r_offset = strlen(lastPos);
memmove(client->recvbuf, lastPos, strlen(lastPos));
memset(&client->recvbuf[client->r_offset], 0, ADC_BUFSIZE-client->r_offset);
return 0;
}
void ADC_client_connect(struct ADC_client* client)
{
struct timeval timeout = { TIMEOUT_IDLE, 0 };
net_connect(client->sd, (struct sockaddr*) &saddr, sizeof(struct sockaddr_in));
set_state_timeout(client, ps_conn);
event_add(&client->ev_read, &timeout);
event_add(&client->ev_write, &timeout);
bot_output(client, "connecting...");
}
void ADC_client_wait_connect(struct ADC_client* client)
{
set_state_timeout(client, ps_none);
}
void ADC_client_disconnect(struct ADC_client* client)
{
if (client->sd != -1)
{
net_close(client->sd);
client->sd = -1;
event_del(&client->ev_read);
event_del(&client->ev_write);
bot_output(client, "disconnected.");
if (running)
{
ADC_client_destroy(client);
ADC_client_create(client, client->num);
ADC_client_connect(client);
}
}
}
static void perf_chat(struct ADC_client* client, int priv)
{
char buf[1024] = { 0, };
size_t r = get_wait_rand(MAX_CHAT_MSGS-1);
char* msg = adc_msg_escape(chat_messages[r]);
if (priv)
{
strcat(buf, "EMSG ");
strcat(buf, sid_to_string(client->sid));
strcat(buf, " ");
strcat(buf, sid_to_string(client->sid));
}
else
{
strcat(buf, "BMSG ");
strcat(buf, sid_to_string(client->sid));
}
strcat(buf, " ");
strcat(buf, msg);
hub_free(msg);
strcat(buf, "\n");
send_client(client, buf);
}
static void perf_search(struct ADC_client* client)
{
char buf[1024] = { 0, };
size_t r = get_wait_rand(MAX_SEARCH_MSGS-1);
size_t pst = get_wait_rand(100);
if (pst > 80)
{
strcat(buf, "FSCH ");
strcat(buf, sid_to_string(client->sid));
strcat(buf, " +TCP4 ");
}
else
{
strcat(buf, "BSCH ");
strcat(buf, sid_to_string(client->sid));
strcat(buf, " ");
}
strcat(buf, search_messages[r]);
strcat(buf, "\n");
send_client(client, buf);
}
static void perf_result(struct ADC_client* client, sid_t target, const char* what, const char* token)
{
char buf[1024] = { 0, };
strcat(buf, "DRES ");
strcat(buf, sid_to_string(client->sid));
strcat(buf, " ");
strcat(buf, sid_to_string(target));
strcat(buf, " FN" "test/");
strcat(buf, what);
strcat(buf, ".dat");
strcat(buf, " SL" "0");
strcat(buf, " SI" "908987128912");
strcat(buf, " TR" "5T6YJYKO3WECS52BKWVSOP5VUG4IKNSZBZ5YHBA");
strcat(buf, " TO");
strcat(buf, token);
strcat(buf, "\n");
send_client(client, buf);
}
static void perf_ctm(struct ADC_client* client)
{
char buf[1024] = { 0, };
strcat(buf, "DCTM ");
strcat(buf, sid_to_string(client->sid));
strcat(buf, " ");
strcat(buf, sid_to_string(client->sid));
strcat(buf, " ");
strcat(buf, "ADC/1.0");
strcat(buf, " TOKEN111");
strcat(buf, sid_to_string(client->sid));
strcat(buf, "\n");
send_client(client, buf);
}
static void perf_update(struct ADC_client* client)
{
char buf[1024] = { 0, };
int n = (int) get_wait_rand(10)+1;
strcat(buf, "BINF ");
strcat(buf, sid_to_string(client->sid));
strcat(buf, " HN");
strcat(buf, uhub_itoa(n));
strcat(buf, "\n");
send_client(client, buf);
}
static void perf_normal_action(struct ADC_client* client)
{
size_t r = get_wait_rand(5);
size_t p = get_wait_rand(100);
switch (r)
{
case 0:
if (p > (90 - (10 * cfg_level)))
{
if (cfg_debug > 1) bot_output(client, "timeout -> disconnect");
ADC_client_disconnect(client);
}
break;
case 1:
if (cfg_chat)
{
if (cfg_debug > 1) bot_output(client, "timeout -> chat");
perf_chat(client, 0);
}
break;
case 2:
if (cfg_debug > 1) bot_output(client, "timeout -> search");
perf_search(client);
break;
case 3:
if (cfg_debug > 1) bot_output(client, "timeout -> update");
perf_update(client);
break;
case 4:
if (cfg_debug > 1) bot_output(client, "timeout -> privmsg");
perf_chat(client, 1);
break;
case 5:
if (cfg_debug > 1) bot_output(client, "timeout -> ctm/rcm");
perf_ctm(client);
break;
}
client_reschedule_timeout(client);
}
void event_callback(int fd, short ev, void *arg)
{
struct ADC_client* client = (struct ADC_client*) arg;
if (ev & EV_READ)
{
if (recv_client(client) == -1)
{
ADC_client_on_disconnected(client);
}
}
if (ev & EV_TIMEOUT)
{
if (client->state == ps_none)
{
if (client->sd == -1)
{
ADC_client_create(client, client->num);
}
ADC_client_connect(client);
}
if (fd == -1)
{
if (client->state == ps_normal && cfg_mode == mode_performance)
{
perf_normal_action(client);
}
}
}
if (ev & EV_WRITE)
{
if (client->state == ps_conn)
{
ADC_client_on_connected(client);
}
else
{
/* FIXME: Call send again */
}
}
}
int ADC_client_create(struct ADC_client* client, int num)
{
struct timeval timeout = { 0, 0 };
memset(client, 0, sizeof(struct ADC_client));
client->num = num;
client->sd = net_socket_create(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (client->sd == -1) return -1;
event_set(&client->ev_write, client->sd, EV_WRITE, event_callback, client);
event_set(&client->ev_read, client->sd, EV_READ | EV_PERSIST, event_callback, client);
net_set_nonblocking(client->sd, 1);
timeout.tv_sec = client->timeout;
evtimer_set(&client->ev_timer, event_callback, client);
set_state_timeout(client, ps_none);
return 0;
}
void ADC_client_destroy(struct ADC_client* client)
{
ADC_client_disconnect(client);
evtimer_del(&client->ev_timer);
}
void runloop(size_t clients)
{
size_t n = 0;
struct ADC_client* client[ADC_MAX_CLIENTS];
for (n = 0; n < clients; n++)
{
struct ADC_client* c = malloc(sizeof(struct ADC_client));
client[n] = c;
ADC_client_create(c, n);
if (n == 0)
{
ADC_client_connect(c);
}
else
{
ADC_client_wait_connect(c);
}
}
event_dispatch();
for (n = 0; n < clients; n++)
{
ADC_client_destroy(client[n]);
free(client[n]);
}
}
static void print_version()
{
printf(ADCRUSH "\n");
printf("Copyright (C) 2008-2009, Jan Vidar Krey\n");
printf("\n");
}
static void print_usage(const char* program)
{
print_version();
printf("Usage: %s <mode> (adc://<host>:<port>) [options]\n", program);
printf("\n");
printf(" Modes\n");
printf(" perf Do performance testing using multiple clients\n");
printf(" bugs Bugs mode, use fuzzer to construct pseudo-random commands.\n");
printf(" security Perform security tests for the hub.\n");
printf(" log Connect one client to the hub and log the output hub.\n");
printf("\n");
printf(" General options\n");
printf(" -c Allow broadcasting chat messages.\n");
printf(" -d Enable debug output.\n");
printf(" -q Quiet mode (no output).\n");
printf("\n");
printf(" Performance options:\n");
printf(" -l <0-3> Level: 0=polite, 1=normal (default), 2=aggressive, 3=excessive.\n");
printf(" -n <num> Number of concurrent connections\n");
printf("\n");
exit(0);
}
int set_defaults()
{
switch (cfg_mode)
{
case mode_performance:
break;
case mode_bugs:
break;
case mode_security:
break;
case mode_log:
cfg_quiet = 0;
cfg_debug = 2;
cfg_clients = 1;
break;
}
return 1;
}
int parse_mode(const char* arg)
{
cfg_mode = 0;
if (!strcmp(arg, "perf"))
cfg_mode = mode_performance;
else if (!strcmp(arg, "bugs"))
cfg_mode = mode_bugs;
else if (!strcmp(arg, "security"))
cfg_mode = mode_security;
else if (!strcmp(arg, "log"))
cfg_mode = mode_log;
return cfg_mode;
}
int parse_address(const char* arg)
{
char* split;
struct hostent* dns;
struct in_addr* addr;
if (!arg)
return 0;
if (strlen(arg) < 9)
return 0;
if (strncmp(arg, "adc://", 6))
return 0;
split = strrchr(arg+6, ':');
if (split == 0 || strlen(split) < 2 || strlen(split) > 6)
return 0;
cfg_port = strtol(split+1, NULL, 10);
if (cfg_port <= 0 || cfg_port > 65535)
return 0;
split[0] = 0;
dns = gethostbyname(arg+6);
if (dns)
{
addr = (struct in_addr*) dns->h_addr_list[0];
cfg_host = strdup(inet_ntoa(*addr));
}
if (!cfg_host)
return 0;
return 1;
}
int parse_arguments(int argc, char** argv)
{
int ok = 1;
int opt;
for (opt = 3; opt < argc; opt++)
{
if (!strcmp(argv[opt], "-c"))
cfg_chat = 1;
else if (!strncmp(argv[opt], "-d", 2))
cfg_debug += strlen(argv[opt]) - 1;
else if (!strcmp(argv[opt], "-q"))
cfg_quiet = 1;
else if (!strcmp(argv[opt], "-l") && (++opt) < argc)
{
cfg_level = MIN(MAX(uhub_atoi(argv[opt]), 0), 3);
}
else if (!strcmp(argv[opt], "-n") && (++opt) < argc)
{
cfg_clients = MIN(MAX(uhub_atoi(argv[opt]), 1), ADC_MAX_CLIENTS);
}
}
return ok;
}
void parse_command_line(int argc, char** argv)
{
if (argc < 2 ||
!parse_mode(argv[1]) ||
!set_defaults() ||
!parse_address(argv[2]) ||
!parse_arguments(argc, argv))
{
print_usage(argv[0]);
}
}
int main(int argc, char** argv)
{
parse_command_line(argc, argv);
net_initialize();
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(cfg_port);
net_string_to_address(AF_INET, cfg_host, &saddr.sin_addr);
runloop(cfg_clients);
net_shutdown();
return 0;
}

555
src/auth.c Normal file
View File

@@ -0,0 +1,555 @@
/*
* 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)
static 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 (!strlen(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 (!strlen(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 (!strlen(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 (strlen(line) == 0)
return 0;
#ifdef ACL_DEBUG
hub_log(log_trace, "acl_parse_line(): '%s'", line);
#endif
line = strip_white_space(line);
if (!strlen(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 (strlen(config->file_acl) == 0) 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_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* password_generate_challenge(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->tm_connected, (int) user->id.sid, (int) user->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;
#ifdef ACL_DEBUG
hub_log(log_trace, "Generating challenge for user %s: '%s'", user->id.nick, tiger_buf);
#endif
return (const char*) tiger_buf;
}
int password_verify(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 password_invalid;
access = acl_get_access_info(user->hub->acl, user->id.nick);
if (!access || !access->password)
return password_invalid;
if (TIGERSIZE+strlen(access->password) >= 1024)
return password_invalid;
challenge = password_generate_challenge(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;
#ifdef ACL_DEBUG
hub_log(log_trace, "Checking password %s against %s", password, password_calc);
#endif
if (strcasecmp(password, password_calc) == 0)
{
return password_ok;
}
return password_invalid;
}

91
src/auth.h Normal file
View File

@@ -0,0 +1,91 @@
/*
* 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 password_status
{
password_invalid = 0,
password_ok = 1,
};
enum acl_status
{
acl_not_found = 0,
acl_found = 1,
};
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)" */
};
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_check_ip_range(struct ip_addr_encap* addr, struct ip_ban_record* info);
extern const char* password_generate_challenge(struct user* user);
extern int password_verify(struct user* user, const char* password);
#endif /* HAVE_UHUB_ACL_H */

127
src/commands.c Normal file
View File

@@ -0,0 +1,127 @@
/*
* 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 command_access_denied(struct user* user)
{
struct adc_message* command;
char* buffer = adc_msg_escape("Access denied.");
command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6);
adc_msg_add_argument(command, buffer);
route_to_user(user, command);
adc_msg_free(command);
hub_free(buffer);
return 0;
}
static int command_stats(struct user* user, const char* message)
{
struct adc_message* command;
if (user->credentials < cred_super)
return command_access_denied(user);
char temp[64];
snprintf(temp, 64, "*** Stats: %u users, peak %u", (unsigned int) user->hub->users->count, (unsigned int) user->hub->users->count_peak);
char* buffer = adc_msg_escape(temp);
command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6);
adc_msg_add_argument(command, buffer);
route_to_user(user, command);
adc_msg_free(command);
hub_free(buffer);
return 0;
}
static int command_help(struct user* user, const char* message)
{
struct adc_message* command;
char* buffer = adc_msg_escape(
"*** Available commands:\n"
"!help - Show this help message\n"
"!stats - Show hub stats (super)\n"
"!version - Show this help message\n"
"!uptime - Display hub uptime\n"
"!kick <user> - Kick user (operator)\n"
);
command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6);
adc_msg_add_argument(command, buffer);
route_to_user(user, command);
adc_msg_free(command);
hub_free(buffer);
return 0;
}
static int command_uptime(struct user* user, const char* message)
{
struct adc_message* command;
char temp[64];
snprintf(temp, 64, "*** Uptime: %s seconds", uhub_itoa((int) difftime(time(0), user->hub->tm_started)));
char* buffer = adc_msg_escape(temp);
command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6);
adc_msg_add_argument(command, buffer);
route_to_user(user, command);
adc_msg_free(command);
hub_free(buffer);
return 0;
}
static int command_kick(struct user* user, const char* message)
{
struct adc_message* command;
if (user->credentials < cred_operator)
return command_access_denied(user);
char* buffer = adc_msg_escape("*** Kick not implemented!");
command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6);
adc_msg_add_argument(command, buffer);
route_to_user(user, command);
adc_msg_free(command);
hub_free(buffer);
return 0;
}
static int command_version(struct user* user, const char* message)
{
struct adc_message* command;
char* buffer = adc_msg_escape("*** Powered by " PRODUCT "/" VERSION);
command = adc_msg_construct(ADC_CMD_IMSG, strlen(buffer) + 6);
adc_msg_add_argument(command, buffer);
route_to_user(user, command);
adc_msg_free(command);
hub_free(buffer);
return 0;
}
int command_dipatcher(struct user* user, const char* message)
{
if (!strncmp(message, "!stats", 6)) command_stats(user, message);
else if (!strncmp(message, "!help", 5)) command_help(user, message);
else if (!strncmp(message, "!kick", 5)) command_kick(user, message);
else if (!strncmp(message, "!version", 8)) command_version(user, message);
else if (!strncmp(message, "!uptime", 7)) command_uptime(user, message);
else
return 1;
return 0;
}

35
src/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 user* user, const char* message);

519
src/config.c Normal file
View File

@@ -0,0 +1,519 @@
/*
* 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 /*|| endptr != &data[strlen(data)-1]*/) { \
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 (strlen(line) == 0) return 0;
#ifdef CONFIG_DUMP
hub_log(log_trace, "config_parse_line(): '%s'", line);
#endif
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 (!strlen(key) || !strlen(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/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: 16384)" */
int max_send_buffer_soft; /**<<< "Max send buffer before message drops, per user (default: 8192)" */
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/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 */

172
src/eventqueue.c Normal file
View File

@@ -0,0 +1,172 @@
/*
* 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();
(*queue)->event = (struct event*) hub_malloc_zero(sizeof(struct event));
if (!(*queue)->q1 || !(*queue)->q2 || !(*queue)->event)
{
list_destroy((*queue)->q1);
list_destroy((*queue)->q2);
return -1;
}
(*queue)->callback = callback;
(*queue)->callback_data = ptr;
evtimer_set((*queue)->event, libevent_queue_process, *queue);
return 0;
}
void event_queue_shutdown(struct event_queue* queue)
{
/* Should be empty at this point! */
list_destroy(queue->q1);
list_destroy(queue->q2);
if (queue->event)
{
evtimer_del(queue->event);
hub_free(queue->event);
}
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);
if (!queue->locked && queue->event)
{
libevent_queue_schedule(queue);
}
}
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);
}
void libevent_queue_schedule(struct event_queue* queue)
{
struct timeval zero = { 0, };
evtimer_add(queue->event, &zero);
}
void libevent_queue_process(int fd, short events, void* arg)
{
struct event_queue* queue = (struct event_queue*) arg;
if (event_queue_process(queue))
{
libevent_queue_schedule(queue);
}
}

55
src/eventqueue.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* 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;
struct event* event; /* libevent handle */
};
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);
/**
* Only used internally with libevent.
*/
extern void libevent_queue_process(int fd, short events, void* arg);
extern void libevent_queue_schedule(struct event_queue* queue);
#endif /* HAVE_UHUB_EVENT_QUEUE_H */

950
src/hub.c Normal file
View File

@@ -0,0 +1,950 @@
/*
* 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 hub_handle_message(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(u, cmd); break;
case ADC_CMD_HPAS: ret = hub_handle_password(u, cmd); break;
case ADC_CMD_BINF: ret = hub_handle_info(u, cmd); break;
#ifdef ADC_UDP_OPERATION
case ADC_CMD_HCHK: ret = hub_handle_autocheck(u, cmd); break;
#endif
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_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:
if (u->hub->config->chat_only && u->credentials < cred_operator)
{
/* These below aren't allowed in chat only hubs */
break;
}
case ADC_CMD_EMSG:
case ADC_CMD_DMSG:
case ADC_CMD_BMSG:
case ADC_CMD_FMSG:
ret = hub_handle_chat_message(u, cmd); break;
default:
if (user_is_logged_in(u))
{
ret = route_message(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 user* u, struct adc_message* cmd)
{
int ret = 0;
int index = 0;
int ok = 1;
char* arg = adc_msg_get_argument(cmd, index);
struct timeval timeout = { TIMEOUT_HANDSHAKE, 0 };
if (u->hub->status == hub_status_disabled && u->state == state_protocol)
{
on_login_failure(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(u);
if (u->ev_read)
event_add(u->ev_read, &timeout);
}
else
{
/* disconnect user. Do not send crap during initial handshake! */
user_disconnect(u, quit_logon_error);
ret = -1;
}
}
return ret;
}
int hub_handle_password(struct user* u, struct adc_message* cmd)
{
char* password = adc_msg_get_argument(cmd, 0);
int ret = 0;
if (u->state == state_verify)
{
if (password_verify(u, password))
{
on_login_success(u);
}
else
{
on_login_failure(u, status_msg_auth_invalid_password);
ret = -1;
}
}
hub_free(password);
return ret;
}
int hub_handle_chat_message(struct user* u, struct adc_message* cmd)
{
char* message = adc_msg_get_argument(cmd, 0);
int ret = 0;
int relay = 1;
/* TODO: Check for hub-commands here. Set relay to 0 and the message will not be sent to other users. */
if (message[0] == '!')
{
relay = command_dipatcher(u, message);
}
if (relay && user_is_logged_in(u))
{
/* adc_msg_remove_named_argument(cmd, "PM"); */
ret = route_message(u, cmd);
}
free(message);
return ret;
}
int on_kick(struct user* u, struct adc_message* cmd)
{
hub_log(log_error, "on_kick() not implemented");
return -1;
}
#ifdef ADC_UDP_OPERATION
int hub_handle_autocheck(struct user* u, struct adc_message* cmd)
{
char* port_str = adc_msg_get_argument(cmd, 0);
char* token = adc_msg_get_argument(cmd, 1);
int port = 0;
if (!port_str || !token || strlen(token) != 4)
{
hub_free(port_str);
hub_free(token);
return -1;
}
port = uhub_atoi(port_str);
if (port == 0 || port > 65535)
{
hub_free(port_str);
hub_free(token);
return -1;
}
hub_send_autocheck(u, port, token);
hub_free(port_str);
hub_free(token);
return 0;
}
#endif
void hub_send_autocheck(struct user* u, uint16_t port, const char* token)
{
}
void hub_send_support(struct user* u)
{
if (user_is_connecting(u) || user_is_logged_in(u))
{
route_to_user(u, u->hub->command_support);
}
}
void hub_send_sid(struct user* u)
{
struct adc_message* command;
if (user_is_connecting(u))
{
command = adc_msg_construct(ADC_CMD_ISID, 10);
u->id.sid = user_manager_get_free_sid(u->hub);
adc_msg_add_argument(command, (const char*) sid_to_string(u->id.sid));
route_to_user(u, command);
adc_msg_free(command);
}
}
void hub_send_ping(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(user, ping);
adc_msg_free(ping);
}
void hub_send_hubinfo(struct user* u)
{
struct adc_message* info = adc_msg_copy(u->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(u->hub)));
adc_msg_add_named_argument(info, "MC", uhub_itoa(hub_get_max_user_count(u->hub)));
adc_msg_add_named_argument(info, "SS", uhub_ulltoa(hub_get_shared_size(u->hub)));
adc_msg_add_named_argument(info, "SF", uhub_itoa(hub_get_shared_files(u->hub)));
/* Maximum/minimum share size */
value = hub_get_max_share(u->hub);
if (value) adc_msg_add_named_argument(info, "XS", uhub_itoa(value));
value = hub_get_min_share(u->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(u->hub);
if (value) adc_msg_add_named_argument(info, "XL", uhub_itoa(value));
value = hub_get_min_slots(u->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(u->hub);
if (value) adc_msg_add_named_argument(info, "XU", uhub_itoa(value));
value = hub_get_min_hubs_user(u->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(u->hub);
if (value) adc_msg_add_named_argument(info, "XR", uhub_itoa(value));
value = hub_get_min_hubs_reg(u->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(u->hub);
if (value) adc_msg_add_named_argument(info, "XO", uhub_itoa(value));
value = hub_get_min_hubs_op(u->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), u->hub->tm_started)));
}
if (user_is_connecting(u) || user_is_logged_in(u))
{
route_to_user(u, info);
}
adc_msg_free(info);
/* Only send banner when connecting */
if (u->hub->config->show_banner && user_is_connecting(u))
{
route_to_user(u, u->hub->command_banner);
}
}
void hub_send_handshake(struct user* u)
{
hub_send_support(u);
hub_send_sid(u);
hub_send_hubinfo(u);
if (!user_is_disconnecting(u))
{
user_set_state(u, state_identify);
}
}
void hub_send_motd(struct user* u)
{
if (u->hub->command_motd)
{
route_to_user(u, u->hub->command_motd);
}
}
void hub_send_password_challenge(struct user* u)
{
struct adc_message* igpa;
igpa = adc_msg_construct(ADC_CMD_IGPA, 38);
adc_msg_add_argument(igpa, password_generate_challenge(u));
user_set_state(u, state_verify);
route_to_user(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;
hub_log(log_trace, "hub_event_dispatcher: %x (ptr=%p)", message->id, message->ptr);
*/
switch (message->id)
{
case UHUB_EVENT_USER_JOIN:
{
if (user_is_disconnecting((struct user*) message->ptr))
break;
if (message->flags)
{
hub_send_password_challenge((struct user*) message->ptr);
}
else
{
on_login_success((struct user*) message->ptr);
}
break;
}
case UHUB_EVENT_USER_QUIT:
{
user_manager_remove((struct user*) message->ptr);
send_quit_message((struct user*) message->ptr);
on_logout_user((struct user*) message->ptr);
user_schedule_destroy((struct user*) message->ptr);
break;
}
case UHUB_EVENT_USER_DESTROY:
{
hub_log(log_trace, "hub_event_dispatcher: UHUB_EVENT_USER_DESTROY (ptr=%p)", message->ptr);
user_destroy((struct user*) message->ptr);
break;
}
default:
/* FIXME: ignored */
break;
}
}
struct hub_info* hub_start_service(struct hub_config* config)
{
struct hub_info* hub = 0;
int server_tcp, ret, ipv6_supported, af;
#ifdef ADC_UDP_OPERATION
int server_udp;
#endif
struct sockaddr_storage addr;
socklen_t sockaddr_size;
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);
}
hub_log(log_info, "Starting server, listening on %s:%d...", address_buf, config->server_port);
server_tcp = net_socket_create(af, SOCK_STREAM, IPPROTO_TCP);
if (server_tcp == -1)
{
hub_free(hub);
return 0;
}
#ifdef ADC_UDP_OPERATION
server_udp = net_socket_create(af, SOCK_DGRAM, IPPROTO_UDP);
if (server_udp == -1)
{
hub_free(hub);
return 0;
}
#endif
ret = net_set_reuseaddress(server_tcp, 1);
if (ret == -1)
{
hub_free(hub);
net_close(server_tcp);
#ifdef ADC_UDP_OPERATION
net_close(server_udp);
#endif
return 0;
}
#ifdef ADC_UDP_OPERATION
ret = net_set_reuseaddress(server_udp, 1);
if (ret == -1)
{
hub_free(hub);
net_close(server_tcp);
net_close(server_udp);
return 0;
}
#endif
ret = net_set_nonblocking(server_tcp, 1);
if (ret == -1)
{
hub_free(hub);
net_close(server_tcp);
#ifdef ADC_UDP_OPERATION
net_close(server_udp);
#endif
return 0;
}
#ifdef ADC_UDP_OPERATION
ret = net_set_nonblocking(server_udp, 1);
if (ret == -1)
{
hub_free(hub);
net_close(server_tcp);
net_close(server_udp);
return 0;
}
#endif
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()));
hub_free(hub);
net_close(server_tcp);
#ifdef ADC_UDP_OPERATION
net_close(server_udp);
#endif
return 0;
}
#ifdef ADC_UDP_OPERATION
ret = net_bind(server_udp, (struct sockaddr*) &addr, sockaddr_size);
if (ret == -1)
{
hub_log(log_fatal, "hub_start_service(): Unable to bind to UDP local address. errno=%d, str=%s", net_error(), net_error_string(net_error()));
hub_free(hub);
net_close(server_tcp);
net_close(server_udp);
return 0;
}
#endif
ret = net_listen(server_tcp, SERVER_BACKLOG);
if (ret == -1)
{
hub_log(log_fatal, "hub_start_service(): Unable to listen to socket");
hub_free(hub);
net_close(server_tcp);
#ifdef ADC_UDP_OPERATION
net_close(server_udp);
#endif
return 0;
}
hub->fd_tcp = server_tcp;
#ifdef ADC_UDP_OPERATION
hub->fd_udp = server_udp;
#endif
hub->config = config;
hub->users = NULL;
if (user_manager_init(hub) == -1)
{
hub_free(hub);
net_close(server_tcp);
#ifdef ADC_UDP_OPERATION
net_close(server_udp);
#endif
return 0;
}
event_set(&hub->ev_accept, hub->fd_tcp, EV_READ | EV_PERSIST, net_on_accept, hub);
if (event_add(&hub->ev_accept, NULL) == -1)
{
user_manager_shutdown(hub);
hub_free(hub);
net_close(server_tcp);
#ifdef ADC_UDP_OPERATION
net_close(server_udp);
#endif
return 0;
}
#ifdef ADC_UDP_OPERATION
event_set(&hub->ev_datagram, hub->fd_udp, EV_READ | EV_PERSIST, net_on_packet, hub);
if (event_add(&hub->ev_datagram, NULL) == -1)
{
user_manager_shutdown(hub);
hub_free(hub);
net_close(server_tcp);
net_close(server_udp);
return 0;
}
#endif
if (event_queue_initialize(&hub->queue, hub_event_dispatcher, (void*) hub) == -1)
{
user_manager_shutdown(hub);
hub_free(hub);
net_close(server_tcp);
#ifdef ADC_UDP_OPERATION
net_close(server_udp);
#endif
return 0;
}
hub->status = hub_status_running;
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);
#ifdef ADC_UDP_OPERATION
event_del(&hub->ev_datagram);
net_close(hub->fd_udp);
#endif
net_close(hub->fd_tcp);
user_manager_shutdown(hub);
hub->status = hub_status_stopped;
hub_free(hub);
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;
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)
{
adc_msg_add_argument(hub->command_banner, "000 Powered\\sby\\s" SERVER);
}
hub->status = (hub->config->hub_enabled ? hub_status_running : hub_status_disabled);
}
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 = 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 = 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 user* user, enum status_message msg, enum msg_status_level level)
{
struct hub_config* cfg = user->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(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";
}
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;
}

326
src/hub.h Normal file
View File

@@ -0,0 +1,326 @@
/*
* 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) */
};
struct hub_info
{
int fd_tcp;
#ifdef ADC_UDP_OPERATION
int fd_udp;
#endif
struct event ev_accept;
struct event ev_timer;
#ifdef ADC_UDP_OPERATION
struct event ev_datagram;
#endif
struct event_queue* queue;
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;
#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 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 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 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 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 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 user* u, struct adc_message* cmd);
/**
* Can only be used by administrators or operators.
*
* @return 0 on success, -1 on error
*/
extern int hub_handle_kick(struct user* u, struct adc_message* cmd);
#ifdef ADC_UDP_OPERATION
/**
* Handle incoming autocheck message.
*/
extern int hub_handle_autocheck(struct user* u, struct adc_message* cmd);
#endif
/**
* Send the support line for the hub to a particular user.
* Only used during the initial handshake.
*/
extern void hub_send_support(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 user* u);
/**
* Send a 'ping' message to user.
*/
extern void hub_send_ping(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 user* u);
/**
* Send handshake. This basically calls
* hub_send_support() and hub_send_sid()
*/
extern void hub_send_handshake(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 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 user* u);
/**
* Send an autocheck message to a user.
* This is basically a UDP message. The user's client can then determine
* if UDP communication works by either hole punching or configuring UPnP.
*/
extern void hub_send_autocheck(struct user* u, uint16_t port, const char* token);
/**
* This starts the hub.
*/
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);
/**
* Sends a status_message to a user.
*/
extern void hub_send_status(struct user* user, enum status_message msg, enum msg_status_level level);
/**
* 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);
#endif /* HAVE_UHUB_HUB_H */

107
src/hubevent.c 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/>.
*
*/
#include "uhub.h"
/* Send MOTD, do logging etc */
void on_login_success(struct user* u)
{
/* Logging - FIXME: Move this to a plugin */
const char* addr = net_get_peer_address(u->sd);
const char* credentials_string[] = { "!none!", "link", "guest", "user", "operator", "super", "admin" };
struct timeval timeout = { TIMEOUT_IDLE, 0 };
/* Send user list of all existing users */
if (!send_user_list(u))
return;
/* Mark as being in the normal state, and add user to the user list */
user_set_state(u, state_normal);
user_manager_add(u);
/* Print log message */
hub_log(log_user, "Login OK %s/%s \"%s\" [%s] (%s) \"%s\"", sid_to_string(u->id.sid), u->id.cid, u->id.nick, addr, credentials_string[u->credentials], u->user_agent);
/* Announce new user to all connected users */
if (user_is_logged_in(u))
route_info_message(u);
/* Send message of the day (if any) */
if (user_is_logged_in(u)) /* Previous send() can fail! */
hub_send_motd(u);
/* reset to idle timeout */
if (u->ev_read)
event_add(u->ev_read, &timeout);
}
void on_login_failure(struct user* u, enum status_message msg)
{
const char* addr = net_get_peer_address(u->sd);
const char* message = hub_get_status_message(u->hub, msg);
hub_log(log_user, "Login FAIL %s/%s \"%s\" [%s] (%s) \"%s\"", sid_to_string(u->id.sid), u->id.cid, u->id.nick, addr, message, u->user_agent);
hub_send_status(u, msg, status_level_fatal);
user_disconnect(u, quit_logon_error);
}
void on_nick_change(struct user* u, const char* nick)
{
if (user_is_logged_in(u))
{
hub_log(log_user, "Nick change %s/%s \"%s\" -> \"%s\"", sid_to_string(u->id.sid), u->id.cid, u->id.nick, nick);
}
}
void on_logout_user(struct user* user)
{
const char* reason = "";
const char* addr;
/* 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;
default:
if (user->hub->status == hub_status_shutdown)
reason = "hub shutdown";
else
reason = "unknown error";
break;
}
addr = ip_convert_to_string(&user->ipaddr);
hub_log(log_user, "Logout %s/%s \"%s\" [%s] (%s)", sid_to_string(user->id.sid), user->id.cid, user->id.nick, addr, reason);
user->quit_reason = 0;
}

45
src/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 user* u);
/**
* This event is triggered whenever a user failed to log in to the hub.
*/
extern void on_login_failure(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 user* u);
/**
* This event is triggered whenever a user changes his/her nickname.
*/
extern void on_nick_change(struct user* u, const char* nick);
#endif /* HAVE_UHUB_HUB_EVENT_H */

859
src/inf.c Normal file
View File

@@ -0,0 +1,859 @@
/*
* 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 user_is_protected(struct user* user);
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 (strlen(it) > 0)
{
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 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 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 user* user, struct adc_message* cmd)
{
int want_ipv4 = 0;
int want_ipv6 = 0;
int nat_override = 0;
const char* address = 0;
if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR))
{
want_ipv6 = 1;
}
if (adc_msg_has_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR))
{
want_ipv4 = 1;
}
if (!want_ipv4 && !want_ipv6)
return 0;
/* Add correct/verified IP addresses instead (if requested/stripped) */
address = (char*) net_get_peer_address(user->sd);
if (address)
{
if (want_ipv4 && strchr(address, '.'))
{
want_ipv6 = 0;
}
else if (want_ipv6)
{
want_ipv4 = 0;
}
/* check if user can do nat override */
if (want_ipv4 && acl_is_ip_nat_override(user->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);
nat_override = 1;
}
hub_free(client_given_ip);
}
}
if (!nat_override)
{
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR);
if (!want_ipv4)
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV4_UDP_PORT);
else
adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV4_ADDR, address);
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR);
if (!want_ipv6)
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_IPV6_UDP_PORT);
else
adc_msg_add_named_argument(cmd, ADC_INF_FLAG_IPV6_ADDR, address);
}
return 0;
}
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 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 user* user, struct adc_message* cmd)
{
struct user* lookup1 = get_user_by_nick(user->hub, user->id.nick);
struct user* lookup2 = get_user_by_cid(user->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);
user_disconnect(lookup1, quit_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 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 user* user, struct adc_message* cmd)
{
if (acl_is_cid_banned(user->hub->acl, user->id.cid))
{
return status_msg_ban_permanently;
}
if (acl_is_user_banned(user->hub->acl, user->id.nick))
{
return status_msg_ban_permanently;
}
if (acl_is_user_denied(user->hub->acl, user->id.nick))
{
return status_msg_inf_error_nick_restricted;
}
return 0;
}
static int check_limits(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))
{
user->hub->users->shared_size -= user->limits.shared_size;
user->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))
{
user->hub->users->shared_files -= user->limits.shared_files;
user->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(user->hub) && hub_get_min_share(user->hub))
{
return status_msg_user_share_size_low;
}
if (user->limits.shared_size > hub_get_max_share(user->hub) && hub_get_max_share(user->hub))
{
return status_msg_user_share_size_high;
}
if ((user->limits.hub_count_user > hub_get_max_hubs_user(user->hub) && hub_get_max_hubs_user(user->hub)) ||
(user->limits.hub_count_registered > hub_get_max_hubs_reg(user->hub) && hub_get_max_hubs_reg(user->hub)) ||
(user->limits.hub_count_operator > hub_get_max_hubs_op(user->hub) && hub_get_max_hubs_op(user->hub)) ||
(user->limits.hub_count_total > hub_get_max_hubs_total(user->hub) && hub_get_max_hubs_total(user->hub)))
{
return status_msg_user_hub_limit_high;
}
if ((user->limits.hub_count_user < hub_get_min_hubs_user(user->hub) && hub_get_min_hubs_user(user->hub)) ||
(user->limits.hub_count_registered < hub_get_min_hubs_reg(user->hub) && hub_get_min_hubs_reg(user->hub)) ||
(user->limits.hub_count_operator < hub_get_min_hubs_op(user->hub) && hub_get_min_hubs_op(user->hub)))
{
return status_msg_user_hub_limit_low;
}
if (user->limits.upload_slots < hub_get_min_slots(user->hub) && hub_get_min_slots(user->hub))
{
return status_msg_user_slots_low;
}
if (user->limits.upload_slots > hub_get_max_slots(user->hub) && hub_get_max_slots(user->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 user* user, struct adc_message* cmd)
{
int ret = 0;
struct user_access_info* info = acl_get_access_info(user->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;
}
/**
* Determines if a user is to be let into the hub even if the hub is "full".
*/
static 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.
*/
static 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 update_user_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;
}
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);
}
static int check_is_hub_full(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 (user->hub->config->max_users && user->hub->users->count >= user->hub->config->max_users && !user_is_protected(user))
{
return 1;
}
return 0;
}
static int check_registered_users_only(struct user* user)
{
if (user->hub->config->registered_users_only && !user_is_registered(user))
{
return 1;
}
return 0;
}
#define INF_CHECK(FUNC, USER, CMD) \
do { \
int ret = FUNC(USER, CMD); \
if (ret < 0) \
return ret; \
} while(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 user* user, struct adc_message* cmd)
{
if (user->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;
}
int hub_handle_info_login(struct user* user, struct adc_message* cmd)
{
int need_auth = 0;
/* Make syntax checks. */
INF_CHECK(check_required_login_flags, user, cmd);
INF_CHECK(check_cid, user, cmd);
INF_CHECK(check_nick, user, cmd);
INF_CHECK(check_network, user, cmd);
INF_CHECK(check_user_agent, user, cmd);
INF_CHECK(check_acl, user, cmd);
INF_CHECK(check_logged_in, user, cmd);
/* Private ID must never be broadcasted - drop it! */
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_PRIVATE_ID);
/* FIXME: This needs some cleaning up */
need_auth = set_credentials(user, cmd);
/* Note: this must be done *after* set_credentials. */
if (check_is_hub_full(user))
{
return status_msg_hub_full;
}
if (check_registered_users_only(user))
{
return status_msg_hub_registered_users_only;
}
INF_CHECK(check_limits, user, cmd);
/* strip off stuff if low_bandwidth_mode is enabled */
hub_handle_info_low_bandwidth(user, cmd);
/* Set initial user info */
user_set_info(user, cmd);
return need_auth;
}
/*
* 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 user* user, const struct adc_message* cmd_unmodified)
{
struct adc_message* cmd = adc_msg_copy(cmd_unmodified); /* FIXME: Have a small memory leak here! */
if (!cmd) return -1; /* OOM */
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))
{
int ret = hub_handle_info_login(user, cmd);
if (ret < 0)
{
on_login_failure(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(user->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(user, cmd))
#endif
adc_msg_remove_named_argument(cmd, ADC_INF_FLAG_NICK);
}
/* FIXME - What if limits are not met ? */
check_limits(user, cmd);
hub_handle_info_low_bandwidth(user, cmd);
update_user_info(user, cmd);
if (!adc_msg_is_empty(cmd))
{
route_message(user, cmd);
}
adc_msg_free(cmd);
}
return 0;
}

54
src/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 user* u, const struct adc_message* cmd);
#endif /* HAVE_UHUB_INF_PARSER_H */

425
src/ipcalc.c Normal file
View File

@@ -0,0 +1,425 @@
/*
* 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 ip_is_valid_ipv4(const char* address)
{
int i = 0; /* address index */
int o = 0; /* octet number */
int n = 0; /* numbers after each dot */
int d = 0; /* dots */
if (!address || strlen(address) > 15 || strlen(address) < 7)
return 0;
for (; i < strlen(address); i++)
{
if (is_num(address[i]))
{
n++;
o *= 10;
o += (address[i] - '0');
}
else if (address[i] == '.')
{
if (n == 0 || n > 3 || o > 255) return 0;
n = 0;
o = 0;
d++;
}
else
{
return 0;
}
}
if (n == 0 || n > 3 || o > 255 || d != 3) return 0;
return 1;
}
int ip_is_valid_ipv6(const char* address)
{
unsigned char buf[16];
int ret = net_string_to_address(AF_INET6, address, buf);
if (ret <= 0) return 0;
return 1;
}
int ip_convert_to_binary(const char* taddr, struct ip_addr_encap* raw)
{
if (ip_is_valid_ipv6(taddr))
{
if (net_string_to_address(AF_INET6, taddr, &raw->internal_ip_data.in6) <= 0)
{
return -1;
}
raw->af = AF_INET6;
return AF_INET6;
}
else if (ip_is_valid_ipv4(taddr))
{
if (net_string_to_address(AF_INET, taddr, &raw->internal_ip_data.in) <= 0)
{
return -1;
}
raw->af = AF_INET;
return AF_INET;
}
return -1;
}
char* ip_convert_to_string(struct ip_addr_encap* raw)
{
static char address[INET6_ADDRSTRLEN+1];
memset(address, 0, INET6_ADDRSTRLEN);
net_address_to_string(raw->af, (void*) &raw->internal_ip_data, address, INET6_ADDRSTRLEN+1);
if (strncmp(address, "::ffff:", 7) == 0) /* IPv6 mapped IPv4 address. */
{
return &address[7];
}
return address;
}
int ip_convert_address(const char* text_address, int port, struct sockaddr* addr, socklen_t* addr_len)
{
struct sockaddr_in6 addr6;
struct sockaddr_in addr4;
size_t sockaddr_size;
const char* taddr = 0;
int ipv6sup = net_is_ipv6_supported();
if (strcmp(text_address, "any") == 0)
{
if (ipv6sup)
{
taddr = "::";
}
else
{
taddr = "0.0.0.0";
}
}
else if (strcmp(text_address, "loopback") == 0)
{
if (ipv6sup)
{
taddr = "::1";
}
else
{
taddr = "127.0.0.1";
}
}
else
{
taddr = text_address;
}
if (ip_is_valid_ipv6(taddr) && ipv6sup)
{
sockaddr_size = sizeof(struct sockaddr_in6);
memset(&addr6, 0, sockaddr_size);
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
if (net_string_to_address(AF_INET6, taddr, &addr6.sin6_addr) <= 0)
{
hub_log(log_fatal, "Unable to convert socket address (ipv6)");
return 0;
}
memcpy(addr, &addr6, sockaddr_size);
*addr_len = sockaddr_size;
}
else if (ip_is_valid_ipv4(taddr))
{
sockaddr_size = sizeof(struct sockaddr_in);
memset(&addr4, 0, sockaddr_size);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(port);
if (net_string_to_address(AF_INET, taddr, &addr4.sin_addr) <= 0)
{
hub_log(log_fatal, "Unable to convert socket address (ipv4)");
return 0;
}
memcpy(addr, &addr4, sockaddr_size);
*addr_len = sockaddr_size;
}
else
{
addr = 0;
*addr_len = 0;
return -1;
}
return 0;
}
int ip_mask_create_left(int af, int bits, struct ip_addr_encap* result)
{
uint32_t mask;
int fill, remain_bits, n;
memset(result, 0, sizeof(struct ip_addr_encap));
result->af = af;
if (bits < 0) bits = 0;
if (af == AF_INET)
{
if (bits > 32) bits = 32;
mask = (0xffffffff << (32 - bits));
if (bits == 0) mask = 0;
result->internal_ip_data.in.s_addr = (((uint8_t*) &mask)[0] << 24) | (((uint8_t*) &mask)[1] << 16) | (((uint8_t*) &mask)[2] << 8) | (((uint8_t*) &mask)[3] << 0);
}
else if (af == AF_INET6)
{
if (bits > 128) bits = 128;
fill = (128-bits) / 8;
remain_bits = (128-bits) % 8;
mask = (0xff << (8 - remain_bits));
n = 0;
for (n = 0; n < fill; n++)
((uint8_t*) &result->internal_ip_data.in6)[n] = (uint8_t) 0xff;
if (fill < 16)
((uint8_t*) &result->internal_ip_data.in6)[fill] = (uint8_t) mask;
}
else
{
return -1;
}
#ifdef IP_CALC_DEBUG
char* r_str = hub_strdup(ip_convert_to_string(result));
hub_log(log_debug, "Created left mask: %s", r_str);
hub_free(r_str);
#endif
return 0;
}
int ip_mask_create_right(int af, int bits, struct ip_addr_encap* result)
{
uint32_t mask;
int fill, remain_bits, n, start;
uint8_t mask8;
memset(result, 0, sizeof(struct ip_addr_encap));
result->af = af;
if (bits < 0) bits = 0;
if (af == AF_INET)
{
if (bits > 32) bits = 32;
mask = (0xffffffff >> (32-bits));
if (bits == 0) mask = 0;
result->internal_ip_data.in.s_addr = (((uint8_t*) &mask)[0] << 24) | (((uint8_t*) &mask)[1] << 16) | (((uint8_t*) &mask)[2] << 8) | (((uint8_t*) &mask)[3] << 0);
}
else if (af == AF_INET6)
{
if (bits > 128) bits = 128;
fill = (128-bits) / 8;
remain_bits = (128-bits) % 8;
mask8 = (0xff >> (8 - remain_bits));
n = 0;
start = 16-fill;
for (n = 0; n < start; n++)
((uint8_t*) &result->internal_ip_data.in6)[n] = (uint8_t) 0x00;
for (n = start; n < 16; n++)
((uint8_t*) &result->internal_ip_data.in6)[n] = (uint8_t) 0xff;
if (start > 0)
((uint8_t*) &result->internal_ip_data.in6)[start-1] = (uint8_t) mask8;
}
else
{
return -1;
}
#ifdef IP_CALC_DEBUG
char* r_str = hub_strdup(ip_convert_to_string(result));
hub_log(log_debug, "Created right mask: %s", r_str);
hub_free(r_str);
#endif
return 0;
}
void ip_mask_apply_AND(struct ip_addr_encap* addr, struct ip_addr_encap* mask, struct ip_addr_encap* result)
{
memset(result, 0, sizeof(struct ip_addr_encap));
result->af = addr->af;
if (addr->af == AF_INET)
{
result->internal_ip_data.in.s_addr = addr->internal_ip_data.in.s_addr & mask->internal_ip_data.in.s_addr;
}
else if (addr->af == AF_INET6)
{
uint32_t A, B, C, D;
int n = 0;
int offset = 0;
for (n = 0; n < 4; n++)
{
offset = n * 4;
A = (((uint8_t*) &addr->internal_ip_data.in6)[offset+0] << 24) |
(((uint8_t*) &addr->internal_ip_data.in6)[offset+1] << 16) |
(((uint8_t*) &addr->internal_ip_data.in6)[offset+2] << 8) |
(((uint8_t*) &addr->internal_ip_data.in6)[offset+3] << 0);
B = (((uint8_t*) &mask->internal_ip_data.in6)[offset+0] << 24) |
(((uint8_t*) &mask->internal_ip_data.in6)[offset+1] << 16) |
(((uint8_t*) &mask->internal_ip_data.in6)[offset+2] << 8) |
(((uint8_t*) &mask->internal_ip_data.in6)[offset+3] << 0);
C = A & B;
D = (((uint8_t*) &C)[0] << 24) |
(((uint8_t*) &C)[1] << 16) |
(((uint8_t*) &C)[2] << 8) |
(((uint8_t*) &C)[3] << 0);
((uint32_t*) &result->internal_ip_data.in6)[n] = D;
}
}
}
void ip_mask_apply_OR(struct ip_addr_encap* addr, struct ip_addr_encap* mask, struct ip_addr_encap* result)
{
memset(result, 0, sizeof(struct ip_addr_encap));
result->af = addr->af;
if (addr->af == AF_INET)
{
result->internal_ip_data.in.s_addr = addr->internal_ip_data.in.s_addr | mask->internal_ip_data.in.s_addr;
}
else if (addr->af == AF_INET6)
{
uint32_t A, B, C, D;
int n = 0;
int offset = 0;
for (n = 0; n < 4; n++)
{
offset = n * 4;
A = (((uint8_t*) &addr->internal_ip_data.in6)[offset+0] << 24) |
(((uint8_t*) &addr->internal_ip_data.in6)[offset+1] << 16) |
(((uint8_t*) &addr->internal_ip_data.in6)[offset+2] << 8) |
(((uint8_t*) &addr->internal_ip_data.in6)[offset+3] << 0);
B = (((uint8_t*) &mask->internal_ip_data.in6)[offset+0] << 24) |
(((uint8_t*) &mask->internal_ip_data.in6)[offset+1] << 16) |
(((uint8_t*) &mask->internal_ip_data.in6)[offset+2] << 8) |
(((uint8_t*) &mask->internal_ip_data.in6)[offset+3] << 0);
C = A | B;
D = (((uint8_t*) &C)[0] << 24) |
(((uint8_t*) &C)[1] << 16) |
(((uint8_t*) &C)[2] << 8) |
(((uint8_t*) &C)[3] << 0);
((uint32_t*) &result->internal_ip_data.in6)[n] = D;
}
}
}
int ip_compare(struct ip_addr_encap* a, struct ip_addr_encap* b)
{
int ret = 0;
uint32_t A, B;
if (a->af == AF_INET)
{
A = (((uint8_t*) &a->internal_ip_data.in.s_addr)[0] << 24) |
(((uint8_t*) &a->internal_ip_data.in.s_addr)[1] << 16) |
(((uint8_t*) &a->internal_ip_data.in.s_addr)[2] << 8) |
(((uint8_t*) &a->internal_ip_data.in.s_addr)[3] << 0);
B = (((uint8_t*) &b->internal_ip_data.in.s_addr)[0] << 24) |
(((uint8_t*) &b->internal_ip_data.in.s_addr)[1] << 16) |
(((uint8_t*) &b->internal_ip_data.in.s_addr)[2] << 8) |
(((uint8_t*) &b->internal_ip_data.in.s_addr)[3] << 0);
ret = A - B;
}
else if (a->af == AF_INET6)
{
int n = 0;
int offset = 0;
for (n = 0; n < 4; n++)
{
offset = n * 4;
A = (((uint8_t*) &a->internal_ip_data.in6)[offset+0] << 24) |
(((uint8_t*) &a->internal_ip_data.in6)[offset+1] << 16) |
(((uint8_t*) &a->internal_ip_data.in6)[offset+2] << 8) |
(((uint8_t*) &a->internal_ip_data.in6)[offset+3] << 0);
B = (((uint8_t*) &b->internal_ip_data.in6)[offset+0] << 24) |
(((uint8_t*) &b->internal_ip_data.in6)[offset+1] << 16) |
(((uint8_t*) &b->internal_ip_data.in6)[offset+2] << 8) |
(((uint8_t*) &b->internal_ip_data.in6)[offset+3] << 0);
if (A == B) continue;
return A - B;
}
return 0;
}
#ifdef IP_CALC_DEBUG
char* a_str = hub_strdup(ip_convert_to_string(a));
char* b_str = hub_strdup(ip_convert_to_string(b));
hub_log(log_debug, "Comparing IPs '%s' AND '%s' => %d", a_str, b_str, ret);
hub_free(a_str);
hub_free(b_str);
#endif
return ret;
}

84
src/ipcalc.h Normal file
View File

@@ -0,0 +1,84 @@
/*
* 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/>.
*
*/
/*
* This file is used for fiddling with IP-addresses,
* primarily used for IP-banning in uhub.
*/
#ifndef HAVE_UHUB_IPCALC_H
#define HAVE_UHUB_IPCALC_H
struct ip_addr_encap {
int af;
union {
struct in_addr in;
struct in6_addr in6;
} internal_ip_data;
};
extern int ip_convert_to_binary(const char* text_addr, struct ip_addr_encap* raw);
extern char* ip_convert_to_string(struct ip_addr_encap* raw);
/*
* @return 1 if address is a valid IPv4 address in text notation
* 0 if invalid
*/
extern int ip_is_valid_ipv4(const char* address);
/*
* @return 1 if address is a valid IPv6 address in text notation
* 0 if invalid
*/
extern int ip_is_valid_ipv6(const char* address);
/*
* This function converts an IP address in text_address to a binary
* struct sockaddr.
* This will auto-detect if the IP-address is IPv6 (and that is supported),
* or if IPv4 should be used.
* NOTE: Use sockaddr_storage to allocate enough memory for IPv6.
*
* @param text_addr is an ipaddress either ipv6 or ipv4.
* Special magic addresses called "any" and "loopback" exist,
* and will work accross IPv6/IPv4.
* @param port Fill the struct sockaddr* with the given port, can safely be ignored.
*/
extern int ip_convert_address(const char* text_address, int port, struct sockaddr* addr, socklen_t* addr_len);
extern int ip_mask_create_left(int af, int bits, struct ip_addr_encap* result);
extern int ip_mask_create_right(int af, int bits, struct ip_addr_encap* result);
extern void ip_mask_apply_AND(struct ip_addr_encap* address, struct ip_addr_encap* mask, struct ip_addr_encap* result);
extern void ip_mask_apply_OR(struct ip_addr_encap* address, struct ip_addr_encap* mask, struct ip_addr_encap* result);
/**
* @return <0 if a is less than b
* @return >0 if a is greater than b
* @return 0 if they are equal
*/
extern int ip_compare(struct ip_addr_encap* a, struct ip_addr_encap* b);
#endif /* HAVE_UHUB_IPCALC_H */

192
src/list.c Normal file
View File

@@ -0,0 +1,192 @@
/*
* 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 linked_list* list_create()
{
struct linked_list* list = NULL;
list = (struct linked_list*) hub_malloc_zero(sizeof(struct linked_list));
if (list == NULL)
return NULL;
return list;
}
void list_destroy(struct linked_list* list)
{
if (list)
hub_free(list);
}
void list_clear(struct linked_list* list, void (*free_handle)(void* ptr))
{
struct node* node = list->first;
struct node* tmp = NULL;
while (node)
{
tmp = node->next;
free_handle(node->ptr);
hub_free(node);
node = tmp;
}
memset(list, 0, sizeof(struct linked_list));
}
void list_append(struct linked_list* list, void* data_ptr)
{
struct node* new_node = (struct node*) hub_malloc_zero(sizeof(struct node));
new_node->ptr = data_ptr;
if (list->last)
{
list->last->next = new_node;
new_node->prev = list->last;
}
else
{
list->first = new_node;
}
list->last = new_node;
list->size++;
}
void list_remove(struct linked_list* list, void* data_ptr)
{
struct node* node = list->first;
assert(data_ptr);
list->iterator = NULL;
while (node)
{
if (node->ptr == data_ptr)
{
if (node->next)
node->next->prev = node->prev;
if (node->prev)
node->prev->next = node->next;
if (node == list->last)
list->last = node->prev;
if (node == list->first)
list->first = node->next;
hub_free(node);
list->size--;
break;
}
node = node->next;
}
}
size_t list_size(struct linked_list* list)
{
return list->size;
}
void* list_get_index(struct linked_list* list, size_t offset)
{
struct node* node = list->first;
size_t n = 0;
for (n = 0; n < list->size; n++)
{
if (n == offset)
{
return node->ptr;
}
node = node->next;
}
return NULL;
}
void* list_get_first(struct linked_list* list)
{
list->iterator = list->first;
if (list->iterator == NULL)
return NULL;
return list->iterator->ptr;
}
struct node* list_get_first_node(struct linked_list* list)
{
list->iterator = list->first;
if (list->iterator == NULL)
return NULL;
return list->iterator;
}
void* list_get_last(struct linked_list* list)
{
list->iterator = list->last;
if (list->iterator == NULL)
return NULL;
return list->iterator->ptr;
}
struct node* list_get_last_node(struct linked_list* list)
{
list->iterator = list->last;
if (list->iterator == NULL)
return NULL;
return list->iterator;
}
void* list_get_next(struct linked_list* list)
{
if (list->iterator == NULL)
list->iterator = list->first;
else
list->iterator = list->iterator->next;
if (list->iterator == NULL)
return NULL;
return list->iterator->ptr;
}
void* list_get_prev(struct linked_list* list)
{
if (list->iterator == NULL)
return NULL;
list->iterator = list->iterator->prev;
if (list->iterator == NULL)
return NULL;
return list->iterator->ptr;
}

61
src/list.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* 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_LINKED_LIST_H
#define HAVE_UHUB_LINKED_LIST_H
struct linked_list
{
size_t size;
struct node* first;
struct node* last;
struct node* iterator;
};
struct node
{
void* ptr;
struct node* next;
struct node* prev;
};
extern struct linked_list* list_create();
extern void list_destroy(struct linked_list*);
extern void list_clear(struct linked_list*, void (*free_handle)(void* ptr) );
extern void list_append(struct linked_list* list, void* data_ptr);
/**
* Remove data_ptr from the list. If multiple versions occur, only the first one is removed.
*/
extern void list_remove(struct linked_list* list, void* data_ptr);
extern size_t list_size(struct linked_list* list);
extern void* list_get_index(struct linked_list*, size_t offset);
extern void* list_get_first(struct linked_list*);
extern void* list_get_last(struct linked_list*);
extern void* list_get_next(struct linked_list*);
extern void* list_get_prev(struct linked_list*);
extern struct node* list_get_first_node(struct linked_list*);
extern struct node* list_get_last_node(struct linked_list*);
#endif /* HAVE_UHUB_LINKED_LIST_H */

231
src/log.c Normal file
View File

@@ -0,0 +1,231 @@
/*
* 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 <locale.h>
#ifndef WIN32
#include <syslog.h>
static int use_syslog = 0;
#endif
static int verbosity = 4;
static FILE* logfile = NULL;
#ifdef MEMORY_DEBUG
static FILE* memfile = NULL;
#define MEMORY_DEBUG_FILE "memlog.txt"
#endif
#ifdef NETWORK_DUMP_DEBUG
#define NETWORK_DUMP_FILE "netdump.log"
static FILE* netdump = NULL;
#endif
static const char* prefixes[] =
{
"FATAL",
"ERROR",
"WARN",
"USER",
"INFO",
"DEBUG",
"TRACE",
"DUMP",
"MEM",
"PROTO",
0
};
void hub_log_initialize(const char* file, int syslog)
{
setlocale(LC_ALL, "C");
#ifdef MEMORY_DEBUG
memfile = fopen(MEMORY_DEBUG_FILE, "w");
if (!memfile)
{
fprintf(stderr, "Unable to create " MEMORY_DEBUG_FILE " for logging memory allocations\n");
return;
}
#endif
#ifdef NETWORK_DUMP_DEBUG
netdump = fopen(NETWORK_DUMP_FILE, "w");
if (!netdump)
{
fprintf(stderr, "Unable to create " NETWORK_DUMP_FILE " for logging network traffic\n");
return;
}
#endif
#ifndef WIN32
if (syslog)
{
use_syslog = 1;
openlog("uhub", LOG_PID, LOG_USER);
}
#endif
if (!file)
{
logfile = stderr;
return;
}
logfile = fopen(file, "a");
if (!logfile)
{
logfile = stderr;
return;
}
}
void hub_log_shutdown()
{
if (logfile && logfile != stderr)
{
fclose(logfile);
logfile = NULL;
}
#ifdef MEMORY_DEBUG
if (memfile)
{
fclose(memfile);
memfile = NULL;
}
#endif
#ifdef NETWORK_DUMP_DEBUG
if (netdump)
{
fclose(netdump);
netdump = NULL;
}
#endif
#ifndef WIN32
if (use_syslog)
{
use_syslog = 0;
closelog();
}
#endif
}
void hub_set_log_verbosity(int verb)
{
verbosity = verb;
}
void hub_log(int log_verbosity, const char *format, ...)
{
static char logmsg[1024];
static char timestamp[32];
struct tm *tmp;
time_t t;
va_list args;
#ifdef MEMORY_DEBUG
if (memfile && log_verbosity == log_memory)
{
va_start(args, format);
vsnprintf(logmsg, 1024, format, args);
va_end(args);
fprintf(memfile, "%s\n", logmsg);
fflush(memfile);
return;
}
#endif
#ifdef NETWORK_DUMP_DEBUG
if (netdump && log_verbosity == log_protocol)
{
va_start(args, format);
vsnprintf(logmsg, 1024, format, args);
va_end(args);
fprintf(netdump, "%s\n", logmsg);
fflush(netdump);
return;
}
#endif
if (log_verbosity < verbosity)
{
t = time(NULL);
tmp = gmtime(&t);
strftime(timestamp, 32, "%a, %d %b %Y %H:%M:%S +0000", tmp);
va_start(args, format);
vsnprintf(logmsg, 1024, format, args);
va_end(args);
if (logfile)
{
fprintf(logfile, "%s %5s: %s\n", timestamp, prefixes[log_verbosity], logmsg);
fflush(logfile);
}
else
{
fprintf(stderr, "%s %5s: %s\n", timestamp, prefixes[log_verbosity], logmsg);
}
}
#ifndef WIN32
if (use_syslog)
{
int level = 0;
if (verbosity < log_info)
return;
va_start(args, format);
vsnprintf(logmsg, 1024, format, args);
va_end(args);
switch (log_verbosity)
{
case log_fatal: level = LOG_CRIT; break;
case log_error: level = LOG_ERR; break;
case log_warning: level = LOG_WARNING; break;
case log_user: level = LOG_INFO | LOG_AUTH; break;
case log_info: level = LOG_INFO; break;
case log_debug: level = LOG_DEBUG; break;
default:
level = 0;
break;
}
if (level == 0)
return;
level |= (LOG_USER | LOG_DAEMON);
syslog(level, "%s", logmsg);
}
#endif
}

59
src/log.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* 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_LOG_H
#define HAVE_UHUB_LOG_H
enum log_verbosity {
log_fatal = 0,
log_error = 1,
log_warning = 2,
log_user = 3,
log_info = 4,
log_debug = 5,
log_trace = 6,
log_dump = 7,
log_memory = 8,
log_protocol = 9,
};
/**
* Specify a minimum log verbosity for what messages should
* be printed in the log.
*/
extern void hub_set_log_verbosity(int log_verbosity);
/**
* Print a message in the log.
*/
extern void hub_log(int log_verbosity, const char *format, ...);
/**
* Initialize the log subsystem, if no output file is given (file is null)
* stderr is assumed by default.
* If the file cannot be opened for writing, stdout is also used.
*/
extern void hub_log_initialize(const char* file, int syslog);
/**
* Shut down and close the logfile.
*/
extern void hub_log_shutdown();
#endif /* HAVE_UHUB_LOG_H */

441
src/main.c Normal file
View File

@@ -0,0 +1,441 @@
/*
* 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 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;
struct timeval now = {0, 0};
switch (signal)
{
case SIGINT:
hub_log(log_info, "Interrupted. Shutting down...");
hub->status = hub_status_shutdown;
event_loopexit(&now);
break;
case SIGTERM:
hub_log(log_info, "Terminated. Shutting down...");
hub->status = hub_status_shutdown;
event_loopexit(&now);
break;
case SIGPIPE:
hub_log(log_trace, "hub_handle_signal(): caught SIGPIPE (ignoring)");
break;
case SIGHUP:
hub_log(log_info, "Caught hangup signal. Reloading configuration files...");
hub->status = hub_status_restart;
event_loopexit(&now);
break;
case SIGUSR1:
hub_log(log_trace, "hub_handle_signal(): caught SIGUSR1 -- FIXME");
break;
case SIGUSR2:
hub_log(log_trace, "hub_handle_signal(): caught SIGUSR2");
{
user_manager_stats(hub);
}
break;
default:
hub_log(log_trace, "hub_handle_signal(): caught unknown signal: %d", signal);
hub->status = hub_status_shutdown;
event_loopexit(&now);
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 */
SIGUSR1, /* dump statistics */
SIGUSR2, /* (unused) */
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);
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 (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);
event_dispatch();
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_shutdown();
hub_log_shutdown();
return 0;
}
int check_configuration(int dump)
{
struct hub_config configuration;
int ret = read_config(arg_config, &configuration, 0);
if (dump)
{
dump_config(&configuration, dump > 1);
puts("");
}
if (ret == -1)
{
fprintf(stderr, "ERROR\n");
return 1;
}
fprintf(stdout, "OK\n");
return 0;
}
void print_version()
{
fprintf(stdout, "" PRODUCT " " VERSION " " PRODUCT_TITLE "\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"
#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:VCsSL")) != -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;
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;
}
#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 */
}
else
{
/* parent process */
hub_log(log_debug, "Forked to background\n");
return 0;
}
}
if (drop_privileges() == -1)
return -1;
#endif /* WIN32 */
ret = main_loop();
return ret;
}

248
src/memory.c Normal file
View File

@@ -0,0 +1,248 @@
/*
* 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 MEMORY_DEBUG
#define REALTIME_MALLOC_TRACKING
#ifdef REALTIME_MALLOC_TRACKING
#define UHUB_MAX_ALLOCS 50000
struct malloc_info
{
void* ptr;
size_t size;
void* stack1;
void* stack2;
};
static int hub_alloc_count = 0;
static size_t hub_alloc_size = 0;
static int hub_alloc_peak_count = 0;
static size_t hub_alloc_peak_size = 0;
static size_t hub_alloc_oom = 0;
static struct malloc_info hub_allocs[UHUB_MAX_ALLOCS] = { { 0, }, };
static int malloc_slot = -1; /* free slot (-1, no slot) */
void internal_debug_print_leaks()
{
size_t n = 0;
size_t leak = 0;
size_t count = 0;
hub_log(log_memory, "--- exit (allocs: %d, size: %zu) ---", hub_alloc_count, hub_alloc_size);
for (; n < UHUB_MAX_ALLOCS; n++)
{
if (hub_allocs[n].ptr)
{
leak += hub_allocs[n].size;
count++;
hub_log(log_memory, "leak %p size: %zu (bt: %p %p)", hub_allocs[n].ptr, hub_allocs[n].size, hub_allocs[n].stack1, hub_allocs[n].stack2);
}
}
hub_log(log_memory, "--- done (allocs: %d, size: %zu, peak: %d/%zu, oom: %zu) ---", count, leak, hub_alloc_peak_count, hub_alloc_peak_size, hub_alloc_oom);
}
#endif /* REALTIME_MALLOC_TRACKING */
void* internal_debug_mem_malloc(size_t size, const char* where)
{
size_t n = 0;
char* ptr = malloc(size);
#ifdef REALTIME_MALLOC_TRACKING
/* Make sure the malloc info struct is initialized */
if (!hub_alloc_count)
{
hub_log(log_memory, "--- start ---");
for (n = 0; n < UHUB_MAX_ALLOCS; n++)
{
hub_allocs[n].ptr = 0;
hub_allocs[n].size = 0;
hub_allocs[n].stack1 = 0;
hub_allocs[n].stack2 = 0;
}
atexit(&internal_debug_print_leaks);
}
if (ptr)
{
if (malloc_slot != -1)
n = (size_t) malloc_slot;
else
n = 0;
for (; n < UHUB_MAX_ALLOCS; n++)
{
if (!hub_allocs[n].ptr)
{
hub_allocs[n].ptr = ptr;
hub_allocs[n].size = size;
hub_allocs[n].stack1 = __builtin_return_address(1);
hub_allocs[n].stack2 = __builtin_return_address(2);
hub_alloc_size += size;
hub_alloc_count++;
hub_alloc_peak_count = MAX(hub_alloc_count, hub_alloc_peak_count);
hub_alloc_peak_size = MAX(hub_alloc_size, hub_alloc_peak_size);
hub_log(log_memory, "%s %p (%d bytes) (bt: %p %p) {allocs: %d, size: %zu}", where, ptr, (int) size, hub_allocs[n].stack1, hub_allocs[n].stack2, hub_alloc_count, hub_alloc_size);
break;
}
}
}
else
{
hub_log(log_memory, "%s *** OOM for %d bytes", where, size);
hub_alloc_oom++;
return 0;
}
#endif /* REALTIME_MALLOC_TRACKING */
return ptr;
}
void internal_debug_mem_free(void* ptr)
{
#ifdef REALTIME_MALLOC_TRACKING
size_t n = 0;
void* stack1 = __builtin_return_address(1);
void* stack2 = __builtin_return_address(2);
if (!ptr) return;
for (; n < UHUB_MAX_ALLOCS; n++)
{
if (hub_allocs[n].ptr == ptr)
{
hub_alloc_size -= hub_allocs[n].size;
hub_alloc_count--;
hub_allocs[n].ptr = 0;
hub_allocs[n].size = 0;
hub_allocs[n].stack1 = 0;
hub_allocs[n].stack2 = 0;
hub_log(log_memory, "free %p (bt: %p %p) {allocs: %d, size: %zu}", ptr, stack1, stack2, hub_alloc_count, hub_alloc_size);
malloc_slot = n;
free(ptr);
return;
}
}
malloc_slot = -1;
abort();
hub_log(log_memory, "free %p *** NOT ALLOCATED *** (bt: %p %p)", ptr, stack1, stack2);
#else
free(ptr);
#endif /* REALTIME_MALLOC_TRACKING */
}
char* debug_mem_strdup(const char* s)
{
size_t size = strlen(s);
char* ptr = internal_debug_mem_malloc(size+1, "strdup");
if (ptr)
{
memcpy(ptr, s, size);
ptr[size] = 0;
}
return ptr;
}
char* debug_mem_strndup(const char* s, size_t n)
{
size_t size = MIN(strlen(s), n);
char* ptr = internal_debug_mem_malloc(size+1, "strndup");
if (ptr)
{
memcpy(ptr, s, size);
ptr[size] = 0;
}
return ptr;
}
void* debug_mem_malloc(size_t size)
{
void* ptr = internal_debug_mem_malloc(size, "malloc");
return ptr;
}
void debug_mem_free(void *ptr)
{
internal_debug_mem_free(ptr);
}
#endif
void* hub_malloc_zero(size_t size)
{
void* data = hub_malloc(size);
if (data)
{
memset(data, 0, size);
}
return data;
}
#ifdef DEBUG_FUNCTION_TRACE
#define FTRACE_LOG "ftrace.log"
static FILE* functrace = 0;
void main_constructor() __attribute__ ((no_instrument_function, constructor));
void main_deconstructor() __attribute__ ((no_instrument_function, destructor));
void __cyg_profile_func_enter(void* frame, void* callsite) __attribute__ ((no_instrument_function));
void __cyg_profile_func_exit(void* frame, void* callsite) __attribute__ ((no_instrument_function));
void main_constructor()
{
functrace = fopen(FTRACE_LOG, "w");
if (functrace == NULL)
{
fprintf(stderr, "Cannot create function trace file: " FTRACE_LOG "\n");
exit(-1);
}
}
void main_deconstructor()
{
fclose(functrace);
}
void __cyg_profile_func_enter(void* frame, void* callsite)
{
if (functrace)
fprintf(functrace, "E%p\n", frame);
}
void __cyg_profile_func_exit(void* frame, void* callsite)
{
if (functrace)
fprintf(functrace, "X%p\n", frame);
}
#endif /* DEBUG_FUNCTION_TRACE */

45
src/memory.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_MEMORY_HANDLER_H
#define HAVE_UHUB_MEMORY_HANDLER_H
#ifdef MEMORY_DEBUG
#define hub_malloc debug_mem_malloc
#define hub_free debug_mem_free
#define hub_strdup debug_mem_strdup
#define hub_strndup debug_mem_strndup
extern void* debug_mem_malloc(size_t size);
extern void debug_mem_free(void* ptr);
extern char* debug_mem_strdup(const char* s);
extern char* debug_mem_strndup(const char* s, size_t n);
#else
#define hub_malloc malloc
#define hub_free free
#define hub_strdup strdup
#define hub_strndup strndup
#endif
extern void* hub_malloc_zero(size_t size);
#endif /* HAVE_UHUB_MEMORY_HANDLER_H */

836
src/message.c Normal file
View File

@@ -0,0 +1,836 @@
/*
* 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 ADC_MSG_ASSERT(X) \
uhub_assert(X); \
uhub_assert(X->cache); \
uhub_assert(X->capacity); \
uhub_assert(X->length); \
uhub_assert(X->length <= X->capacity); \
uhub_assert(X->length == strlen(X->cache)); \
uhub_assert(X->references >= 0);
#else
#define ADC_MSG_ASSERT(X) do { } while(0)
#endif
struct adc_message* adc_msg_incref(struct adc_message* msg)
{
#ifndef ADC_MESSAGE_INCREF
msg->references++;
return msg;
#else
struct adc_message* copy = adc_msg_copy(msg);
return copy;
#endif
}
static void adc_msg_set_length(struct adc_message* msg, size_t len)
{
msg->length = len;
}
static int adc_msg_grow(struct adc_message* msg, size_t size)
{
char* buf;
size_t newsize = 0;
if (msg->capacity > size)
return 1;
/* Make sure we align our data */
newsize = size;
newsize += 2; /* termination */
newsize += (newsize % sizeof(size_t)); /* alignment padding */
buf = hub_malloc_zero(newsize);
if (!buf)
return 0;
if (msg->cache)
{
memcpy(buf, msg->cache, msg->length);
hub_free(msg->cache);
}
msg->cache = buf;
msg->capacity = newsize;
return 1;
}
/* NOTE: msg must be unterminated here */
static int adc_msg_cache_append(struct adc_message* msg, const char* string, size_t len)
{
if (!adc_msg_grow(msg, msg->length + len))
{
/* FIXME: OOM! */
return 0;
}
memcpy(&msg->cache[msg->length], string, len);
adc_msg_set_length(msg, msg->length + len);
assert(msg->capacity > msg->length);
msg->cache[msg->length] = 0;
return 1;
}
/**
* Returns position of the first argument of the message.
* Excludes mandatory arguments for the given type of message
* like source and target.
*/
int adc_msg_get_arg_offset(struct adc_message* msg)
{
if (!msg || !msg->cache)
return -1;
switch (msg->cache[0])
{
/* These *SHOULD* never be seen on a hub */
case 'U':
case 'C':
return 4; /* Actually: 4 + strlen(cid). */
case 'I':
case 'H':
return 4;
case 'B':
return 9;
case 'F':
return (10 + (list_size(msg->feature_cast_include)*5) + (list_size(msg->feature_cast_exclude)*5));
case 'D':
case 'E':
return 14;
}
return -1;
}
int adc_msg_is_empty(struct adc_message* msg)
{
int offset = adc_msg_get_arg_offset(msg);
if (offset == -1)
return -1;
if ((msg->length - 1) == (size_t) offset)
return 1;
return 0;
}
void adc_msg_free(struct adc_message* msg)
{
if (!msg) return;
ADC_MSG_ASSERT(msg);
if (msg->references > 0)
{
msg->references--;
}
else
{
hub_free(msg->cache);
if (msg->feature_cast_include)
{
list_clear(msg->feature_cast_include, &hub_free);
list_destroy(msg->feature_cast_include);
msg->feature_cast_include = 0;
}
if (msg->feature_cast_exclude)
{
list_clear(msg->feature_cast_exclude, &hub_free);
list_destroy(msg->feature_cast_exclude);
msg->feature_cast_exclude = 0;
}
hub_free(msg);
}
}
struct adc_message* adc_msg_copy(const struct adc_message* cmd)
{
char* tmp = 0;
struct adc_message* copy = (struct adc_message*) hub_malloc_zero(sizeof(struct adc_message));
if (!copy) return NULL; /* OOM */
ADC_MSG_ASSERT(cmd);
/* deep copy */
copy->cmd = cmd->cmd;
copy->source = cmd->source;
copy->target = cmd->target;
copy->cache = 0;
copy->length = cmd->length;
copy->capacity = 0;
copy->priority = cmd->priority;
copy->references = 0;
copy->feature_cast_include = 0;
copy->feature_cast_exclude = 0;
if (cmd->cache)
{
if (!adc_msg_grow(copy, copy->length))
{
adc_msg_free(copy);
return NULL; /* OOM */
}
memcpy(copy->cache, cmd->cache, cmd->length);
copy->cache[copy->length] = 0;
}
if (cmd->feature_cast_include)
{
copy->feature_cast_include = list_create();
tmp = list_get_first(cmd->feature_cast_include);
while (tmp)
{
list_append(copy->feature_cast_include, hub_strdup(tmp));
tmp = list_get_next(cmd->feature_cast_include);
}
}
if (cmd->feature_cast_exclude)
{
copy->feature_cast_exclude = list_create();
tmp = list_get_first(cmd->feature_cast_exclude);
while (tmp)
{
list_append(copy->feature_cast_exclude, hub_strdup(tmp));
tmp = list_get_next(cmd->feature_cast_exclude);
}
}
ADC_MSG_ASSERT(copy);
return copy;
}
struct adc_message* adc_msg_parse_verify(struct user* u, const char* line, size_t length)
{
struct adc_message* command = adc_msg_parse(line, length);
if (!command)
return 0;
if (command->source && (!u || command->source != u->id.sid))
{
adc_msg_free(command);
return 0;
}
return command;
}
struct adc_message* adc_msg_parse(const char* line, size_t length)
{
struct adc_message* command = (struct adc_message*) hub_malloc_zero(sizeof(struct adc_message));
char prefix = line[0];
size_t n = 0;
char temp_sid[5];
int ok = 1;
int need_terminate = 0;
struct linked_list* feature_cast_list;
if (command == NULL)
return NULL; /* OOM */
if (line[length-1] != '\n')
{
need_terminate = 1;
}
if (!adc_msg_grow(command, length + need_terminate))
{
hub_free(command);
return NULL; /* OOM */
}
adc_msg_set_length(command, length + need_terminate);
memcpy(command->cache, line, length);
/* Ensure we are zero terminated */
command->cache[length] = 0;
command->cache[length+need_terminate] = 0;
command->cmd = FOURCC(line[0], line[1], line[2], line[3]);
command->priority = 0;
switch (prefix)
{
case 'U':
case 'C':
/* these should never be seen on a hub */
ok = 0;
break;
case 'I':
case 'H':
ok = (length > 3);
break;
case 'B':
ok = (length > 8 &&
is_space(line[4]) &&
is_valid_base32_char(line[5]) &&
is_valid_base32_char(line[6]) &&
is_valid_base32_char(line[7]) &&
is_valid_base32_char(line[8]));
if (!ok) break;
temp_sid[0] = line[5];
temp_sid[1] = line[6];
temp_sid[2] = line[7];
temp_sid[3] = line[8];
temp_sid[4] = '\0';
command->source = string_to_sid(temp_sid);
break;
case 'F':
ok = (length > 8 &&
is_space(line[4]) &&
is_valid_base32_char(line[5]) &&
is_valid_base32_char(line[6]) &&
is_valid_base32_char(line[7]) &&
is_valid_base32_char(line[8]));
if (!ok) break;
temp_sid[0] = line[5];
temp_sid[1] = line[6];
temp_sid[2] = line[7];
temp_sid[3] = line[8];
temp_sid[4] = '\0';
command->source = string_to_sid(temp_sid);
/* Create feature cast lists */
command->feature_cast_include = list_create();
command->feature_cast_exclude = list_create();
if (!command->feature_cast_include || !command->feature_cast_exclude)
{
list_destroy(command->feature_cast_include);
list_destroy(command->feature_cast_exclude);
hub_free(command->cache);
hub_free(command);
return NULL; /* OOM */
}
n = 10;
while (line[n] == '+' || line[n] == '-')
{
if (line[n++] == '+')
feature_cast_list = command->feature_cast_include;
else
feature_cast_list = command->feature_cast_exclude;
temp_sid[0] = line[n++];
temp_sid[1] = line[n++];
temp_sid[2] = line[n++];
temp_sid[3] = line[n++];
temp_sid[4] = '\0';
list_append(feature_cast_list, hub_strdup(temp_sid));
}
if (n == 10)
ok = 0;
break;
case 'D':
case 'E':
ok = (length > 13 &&
is_space(line[4]) &&
is_valid_base32_char(line[5]) &&
is_valid_base32_char(line[6]) &&
is_valid_base32_char(line[7]) &&
is_valid_base32_char(line[8]) &&
is_space(line[9]) &&
is_valid_base32_char(line[10]) &&
is_valid_base32_char(line[11]) &&
is_valid_base32_char(line[12]) &&
is_valid_base32_char(line[13]));
if (!ok) break;
temp_sid[0] = line[5];
temp_sid[1] = line[6];
temp_sid[2] = line[7];
temp_sid[3] = line[8];
temp_sid[4] = '\0';
command->source = string_to_sid(temp_sid);
temp_sid[0] = line[10];
temp_sid[1] = line[11];
temp_sid[2] = line[12];
temp_sid[3] = line[13];
temp_sid[4] = '\0';
command->target = string_to_sid(temp_sid);
break;
default:
ok = 0;
}
if (need_terminate)
{
command->cache[length] = '\n';
}
if (!ok)
{
adc_msg_free(command);
return NULL;
}
/* At this point the arg_offset should point to a space, or the end of message */
n = adc_msg_get_arg_offset(command);
if (command->cache[n] == ' ')
{
if (command->cache[n+1] == ' ') ok = 0;
}
else if (command->cache[n] == '\n') ok = 1;
else ok = 0;
if (!ok)
{
adc_msg_free(command);
return NULL;
}
ADC_MSG_ASSERT(command);
return command;
}
struct adc_message* adc_msg_create(const char* line)
{
return adc_msg_parse_verify(NULL, line, strlen(line));
}
struct adc_message* adc_msg_construct(fourcc_t fourcc, size_t size)
{
struct adc_message* msg = (struct adc_message*) hub_malloc_zero(sizeof(struct adc_message));
if (!msg)
return NULL; /* OOM */
if (size < sizeof(fourcc)) size = sizeof(fourcc);
if (!adc_msg_grow(msg, size+1))
{
hub_free(msg);
return NULL; /* OOM */
}
if (fourcc)
{
msg->cache[0] = (char) ((fourcc >> 24) & 0xff);
msg->cache[1] = (char) ((fourcc >> 16) & 0xff);
msg->cache[2] = (char) ((fourcc >> 8) & 0xff);
msg->cache[3] = (char) ((fourcc ) & 0xff);
msg->cache[4] = '\n';
/* Ensure we are zero terminated */
adc_msg_set_length(msg, 5);
msg->cache[msg->length] = 0;
}
msg->cmd = fourcc;
msg->priority = 0;
return msg;
}
int adc_msg_remove_named_argument(struct adc_message* cmd, const char prefix_[2])
{
char* start;
char* end;
char* endInfo;
size_t endlen;
char prefix[4] = { ' ', prefix_[0], prefix_[1], '\0' };
int found = 0;
int arg_offset = adc_msg_get_arg_offset(cmd);
size_t temp_len = 0;
adc_msg_unterminate(cmd);
start = memmem(&cmd->cache[arg_offset], (cmd->length - arg_offset), prefix, 3);
while (start)
{
endInfo = &cmd->cache[cmd->length];
if (&start[0] < &endInfo[0])
{
end = memchr(&start[1], ' ', &endInfo[0]-&start[1]);
}
else
{
end = NULL;
}
if (end)
{
temp_len = &end[0] - &start[0]; // strlen(start);
/* hub_log(log_trace, " length=%d", (int) (temp_len)); */
endlen = strlen(end);
memmove(start, end, endlen);
start[endlen] = '\0';
found++;
adc_msg_set_length(cmd, cmd->length - temp_len);
}
else
{
found++;
adc_msg_set_length(cmd, cmd->length - strlen(start));
start[0] = '\0';
break;
}
start = memmem(&cmd->cache[arg_offset], (cmd->length - arg_offset), prefix, 3);
}
adc_msg_terminate(cmd);
return found;
}
int adc_msg_has_named_argument(struct adc_message* cmd, const char prefix_[2])
{
int count = 0;
char* start;
char prefix[4] = { ' ', prefix_[0], prefix_[1], '\0' };
int arg_offset = adc_msg_get_arg_offset(cmd);
ADC_MSG_ASSERT(cmd);
start = memmem(&cmd->cache[arg_offset], (cmd->length - arg_offset), prefix, 3);
while (start)
{
count++;
if ((&start[0] - &cmd->cache[0]) < 1+cmd->length)
start = memmem(&start[1], (&cmd->cache[cmd->length] - &start[0]), prefix, 3);
else
start = NULL;
}
return count;
}
char* adc_msg_get_named_argument(struct adc_message* cmd, const char prefix_[2])
{
char* start;
char* end;
char* argument;
size_t length;
char prefix[4] = { ' ', prefix_[0], prefix_[1], '\0' };
int arg_offset = adc_msg_get_arg_offset(cmd);
ADC_MSG_ASSERT(cmd);
start = memmem(&cmd->cache[arg_offset], cmd->length - arg_offset, prefix, 3);
if (!start)
return NULL;
start = &start[3];
end = strchr(start, ' ');
if (!end) end = &cmd->cache[cmd->length];
length = &end[0] - &start[0];
argument = hub_strndup(start, length);
if (length > 0 && argument[length-1] == '\n')
{
argument[length-1] = 0;
}
return argument;
}
int adc_msg_replace_named_argument(struct adc_message* cmd, const char prefix[2], const char* string)
{
ADC_MSG_ASSERT(cmd);
while (adc_msg_has_named_argument(cmd, prefix))
{
adc_msg_remove_named_argument(cmd, prefix);
}
if (adc_msg_add_named_argument(cmd, prefix, string) == -1)
{
return -1;
}
ADC_MSG_ASSERT(cmd);
return 0;
}
void adc_msg_terminate(struct adc_message* cmd)
{
if (cmd->cache[cmd->length - 1] != '\n')
{
adc_msg_cache_append(cmd, "\n", 1);
}
ADC_MSG_ASSERT(cmd);
}
/* FIXME: this looks bogus */
void adc_msg_unterminate(struct adc_message* cmd)
{
ADC_MSG_ASSERT(cmd);
if (cmd->length > 0 && cmd->cache[cmd->length-1] == '\n')
{
cmd->length--;
cmd->cache[cmd->length] = 0;
}
}
int adc_msg_add_named_argument(struct adc_message* cmd, const char prefix[2], const char* string)
{
int ret = 0;
if (!string)
return -1;
ADC_MSG_ASSERT(cmd);
adc_msg_unterminate(cmd);
adc_msg_cache_append(cmd, " ", 1);
adc_msg_cache_append(cmd, prefix, 2);
adc_msg_cache_append(cmd, string, strlen(string));
adc_msg_terminate(cmd);
return ret;
}
int adc_msg_add_argument(struct adc_message* cmd, const char* string)
{
ADC_MSG_ASSERT(cmd);
adc_msg_unterminate(cmd);
adc_msg_cache_append(cmd, " ", 1);
adc_msg_cache_append(cmd, string, strlen(string));
adc_msg_terminate(cmd);
return 0;
}
char* adc_msg_get_argument(struct adc_message* cmd, int offset)
{
char* start;
char* end;
char* argument;
int count = 0;
ADC_MSG_ASSERT(cmd);
adc_msg_unterminate(cmd);
start = strchr(&cmd->cache[adc_msg_get_arg_offset(cmd)-1], ' ');
while (start)
{
end = strchr(&start[1], ' ');
if (count == offset)
{
if (end)
{
argument = hub_strndup(&start[1], (&end[0] - &start[1]));
}
else
{
argument = hub_strdup(&start[1]);
if (argument[strlen(argument)-1] == '\n')
argument[strlen(argument)-1] = 0;
}
if (strlen(argument))
{
adc_msg_terminate(cmd);
return argument;
}
}
count++;
start = end;
}
adc_msg_terminate(cmd);
return 0;
}
/**
* NOTE: Untested code.
*/
int adc_msg_get_argument_index(struct adc_message* cmd, const char prefix[2])
{
char* start;
char* end;
int count = 0;
ADC_MSG_ASSERT(cmd);
adc_msg_unterminate(cmd);
start = strchr(&cmd->cache[adc_msg_get_arg_offset(cmd)-1], ' ');
while (start)
{
end = strchr(&start[1], ' ');
if (((&end[0] - &start[1]) > 2) && ((start[1] == prefix[0]) && (start[2] == prefix[1])))
{
adc_msg_terminate(cmd);
return count;
}
count++;
start = end;
}
adc_msg_terminate(cmd);
return -1;
}
int adc_msg_escape_length(const char* str)
{
int add = 0;
int n = 0;
for (; str[n]; n++)
if (str[n] == ' ' || str[n] == '\n' || str[n] == '\\') add++;
return n + add;
}
int adc_msg_unescape_length(const char* str)
{
int add = 0;
int n = 0;
int escape = 0;
for (; str[n]; n++)
{
if (escape)
{
escape = 0;
}
else
{
if (str[n] == '\\')
{
escape = 1;
add++;
}
}
}
return n - add;
}
char* adc_msg_unescape(const char* string)
{
char* new_string = hub_malloc(adc_msg_unescape_length(string)+1);
char* ptr = (char*) new_string;
char* str = (char*) string;
int escaped = 0;
while (*str)
{
if (escaped) {
if (*str == 's')
*ptr++ = ' ';
else if (*str == '\\')
*ptr++ = '\\';
else if (*str == 'n')
*ptr++ = '\n';
else
*ptr++ = *str;
escaped = 0;
} else {
if (*str == '\\')
escaped = 1;
else
*ptr++ = *str;
}
str++;
}
*ptr = 0;
return new_string;
}
char* adc_msg_escape(const char* string)
{
char* str = hub_malloc(adc_msg_escape_length(string)+1);
int n = 0;
int i = 0;
for (i = 0; i < strlen(string); i++)
{
switch (string[i]) {
case '\\': /* fall through */
str[n++] = '\\';
str[n++] = '\\';
break;
case '\n':
str[n++] = '\\';
str[n++] = 'n';
break;
case ' ':
str[n++] = '\\';
str[n++] = 's';
break;
default:
str[n++] = string[i];
break;
}
}
str[n] = '\0';
return str;
}

206
src/message.h Normal file
View File

@@ -0,0 +1,206 @@
/*
* 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_COMMAND_H
#define HAVE_UHUB_COMMAND_H
struct user;
struct adc_message
{
fourcc_t cmd;
sid_t source;
sid_t target;
char* cache;
size_t length;
size_t capacity;
size_t priority;
size_t references;
struct linked_list* feature_cast_include;
struct linked_list* feature_cast_exclude;
};
enum msg_status_level
{
status_level_info = 0, /* Success/informative status message */
status_level_error = 1, /* Recoverable error */
status_level_fatal = 2, /* Fatal error (disconnect) */
};
/**
* Increase the reference counter for an ADC message struct.
* NOTE: Always use the returned value, and not the passed value, as
* it ensures we can actually copy the value if needed.
*/
extern struct adc_message* adc_msg_incref(struct adc_message* msg);
/**
* Decrease the reference counter, and free the memory when apropriate.
*/
extern void adc_msg_free(struct adc_message* msg);
/**
* Perform deep copy a command.
* NOTE: 'references' will be zero for the copied command.
* @return a copy of cmd or NULL if not able to allocate memory.
*/
extern struct adc_message* adc_msg_copy(const struct adc_message* cmd);
/**
* This will parse 'string' and return it as a adc_message struct, or
* NULL if not able to allocate memory or 'string' does not contain
* a valid ADC command.
*
* The message is only considered valid if the user who sent it
* is the rightful origin of the message.
*/
extern struct adc_message* adc_msg_parse_verify(struct user* u, const char* string, size_t length);
/**
* This will parse 'string' and return it as a adc_message struct, or
* NULL if not able to allocate memory or 'string' does not contain
* a valid ADC command.
*/
extern struct adc_message* adc_msg_parse(const char* string, size_t length);
/**
* This will construct a adc_message based on 'string'.
* Only to be used for server generated commands.
*/
extern struct adc_message* adc_msg_create(const char* string);
/**
* Construct a message with the given 'fourcc' and allocate
* 'size' bytes for later use.
*/
extern struct adc_message* adc_msg_construct(fourcc_t fourcc, size_t size);
/**
* Remove a named argument from the command.
*
* @arg prefix a 2 character argument prefix
* @return the number of named arguments removed.
*/
extern int adc_msg_remove_named_argument(struct adc_message* cmd, const char prefix[2]);
/**
* Count the number of arguments matching the given 2 character prefix.
*
* @arg prefix a 2 character argument prefix
* @return the number of matching arguments
*/
extern int adc_msg_has_named_argument(struct adc_message* cmd, const char prefix[2]);
/**
* Returns a named arguments based on the 2 character prefix.
* If multiple matching arguments exists, only the first one will be returned
* by this function.
*
* NOTE: Returned memory must be free'd with hub_free().
*
* @arg prefix a 2 character argument prefix
* @return the argument or NULL if OOM/not found.
*/
extern char* adc_msg_get_named_argument(struct adc_message* cmd, const char prefix[2]);
/**
* Returns a offset of an argument based on the 2 character prefix.
* If multiple matching arguments exists, only the first one will be returned
* by this function.
*
* @arg prefix a 2 character argument prefix
* @return the offset or -1 if the argument is not found.
*/
extern int adc_msg_get_named_argument_index(struct adc_message* cmd, const char prefix[2]);
/**
* @param cmd command to be checked
* @return 1 if the command does not have any arguments (parameters), 0 otherwise, -1 if cmd is invalid.
*/
extern int adc_msg_is_empty(struct adc_message* cmd);
/**
* Returns the argument on the offset position in the command.
* If offset is invalid NULL is returned.
*
* NOTE: Returned memory must be free'd with hub_free().
*
* @return the argument or NULL if OOM/not found.
*/
extern char* adc_msg_get_argument(struct adc_message* cmd, int offset);
/**
* Replace a named argument in the command.
* This will remove any matching arguments (multiple, or none),
* then add 'string' as an argument using the given prefix.
*
* @arg prefix a 2 character argument prefix
* @arg string must be escaped (see adc_msg_escape).
* @return 0 if successful, or -1 if an error occured.
*/
extern int adc_msg_replace_named_argument(struct adc_message* cmd, const char prefix[2], const char* string);
/**
* Append an argument
*
* @arg string must be escaped (see adc_msg_escape).
* @return 0 if successful, or -1 if an error occured (out of memory).
*/
extern int adc_msg_add_argument(struct adc_message* cmd, const char* string);
/**
* Append a named argument
*
* @arg prefix a 2 character argument prefix
* @arg string must be escaped (see adc_msg_escape).
* @return 0 if successful, or -1 if an error occured (out of memory).
*/
extern int adc_msg_add_named_argument(struct adc_message* cmd, const char prefix[2], const char* string);
/**
* Convert a ADC command escaped string to a regular string.
* @return string or NULL if out of memory
*/
extern char* adc_msg_unescape(const char* string);
/**
* Convert a string to a ADC command escaped string.
* @return adc command escaped string or NULL if out of memory.
*/
extern char* adc_msg_escape(const char* string);
/**
* This will ensure a newline is at the end of the command.
*/
void adc_msg_terminate(struct adc_message* cmd);
/**
* This will remove any newline from the end of the command
*/
void adc_msg_unterminate(struct adc_message* cmd);
/**
* @return the offset for the first command argument in msg->cache.
* or -1 if the command is not understood.
* NOTE: for 'U' and 'C' commands (normally not seen by hubs),
* this returns 4. Should be 4 + lengthOf(cid).
*/
int adc_msg_get_arg_offset(struct adc_message* msg);
#endif /* HAVE_UHUB_COMMAND_H */

299
src/misc.c Normal file
View File

@@ -0,0 +1,299 @@
/*
* 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 is_space(char c)
{
if (c == ' ') return 1;
return 0;
}
int is_white_space(char c)
{
if (c == ' ' || c == '\t' || c == '\r') return 1;
return 0;
}
char* strip_white_space(char* string)
{
char* pos;
while (string[0] && is_white_space(string[0])) string++;
if (!strlen(string))
return string;
/* Strip appending whitespace */
pos = &string[strlen(string)-1];
while (&string[0] < &pos[0] && is_white_space(pos[0])) { pos[0] = 0; pos--; }
return string;
}
int is_valid_utf8(const char* string)
{
int expect = 0;
char div = 0;
int pos = 0;
int length = strlen(string);
if (length == 0) return 1;
for (pos = 0; pos < strlen(string); pos++)
{
if (expect)
{
if ((string[pos] & 0xC0) == 0x80) expect--;
else return 0;
}
else
{
if (string[pos] & 0x80)
{
for (div = 0x40; div > 0x10; div /= 2)
{
if (string[pos] & div) expect++;
else break;
}
if ((string[pos] & div) || (pos+expect >= strlen(string))) return 0;
}
}
}
return 1;
}
int is_valid_base32_char(char c)
{
if ((c >= 'A' && c <= 'Z') || (c >= '2' && c <= '7')) return 1;
return 0;
}
int is_num(char c)
{
if (c >= '0' && c <= '9') return 1;
return 0;
}
void base32_encode(const unsigned char* buffer, size_t len, char* result) {
unsigned char word = 0;
size_t n = 0;
size_t i = 0;
size_t index = 0;
for (; i < len;) {
if (index > 3) {
word = (buffer[i] & (0xFF >> index));
index = (index + 5) % 8;
word <<= index;
if (i < len - 1)
word |= buffer[i + 1] >> (8 - index);
i++;
} else {
word = (buffer[i] >> (8 - (index + 5))) & 0x1F;
index = (index + 5) % 8;
if (index == 0) i++;
}
result[n++] = BASE32_ALPHABET[word];
}
result[n] = '\0';
}
void base32_decode(const char* src, unsigned char* dst, size_t len) {
size_t index = 0;
size_t offset = 0;
size_t i = 0;
memset(dst, 0, len);
for (i = 0; src[i]; i++) {
unsigned char n = 0;
for (; n < 32; n++) if (src[i] == BASE32_ALPHABET[n]) break;
if (n == 32) continue;
if (index <= 3) {
index = (index + 5) % 8;
if (index == 0) {
dst[offset++] |= n;
if (offset == len) break;
} else {
dst[offset] |= n << (8 - index);
}
} else {
index = (index + 5) % 8;
dst[offset++] |= (n >> index);
if (offset == len) break;
dst[offset] |= n << (8 - index);
}
}
}
int file_read_lines(const char* file, void* data, file_line_handler_t handler)
{
int fd;
ssize_t ret;
char buf[MAX_RECV_BUF];
char *pos, *start;
size_t line_count = 0;
memset(buf, 0, MAX_RECV_BUF);
hub_log(log_trace, "Opening file %s for line reading.", file);
fd = open(file, 0);
if (fd == -1)
{
hub_log(log_error, "Unable to open file %s: %s", file, strerror(errno));
return -2;
}
ret = read(fd, buf, MAX_RECV_BUF);
if (ret < 0)
{
hub_log(log_error, "Unable to read from file %s: %s", file, strerror(errno));
close(fd);
return -1;
}
else if (ret == 0)
{
close(fd);
hub_log(log_warning, "File is empty.");
return 0;
}
else
{
close(fd);
/* Parse configuaration */
start = buf;
while ((pos = strchr(start, '\n')))
{
pos[0] = '\0';
if (strlen(start) > 0)
{
hub_log(log_dump, "Line: %s", start);
if (handler(start, line_count+1, data) < 0)
return -1;
}
start = &pos[1];
line_count++;
}
if (strlen(start) > 0)
{
buf[strlen(start)] = 0;
hub_log(log_dump, "Line: %s", start);
if (handler(start, line_count+1, data) < 0)
return -1;
}
}
return line_count+1;
}
int uhub_atoi(const char* value) {
int len = strlen(value);
int offset = 0;
int val = 0;
int i = 0;
for (; i < len; i++)
if (value[i] > '9' || value[i] < '0')
offset++;
for (i = offset; i< len; i++)
val = val*10 + (value[i] - '0');
return value[0] == '-' ? -val : val;
}
/*
* FIXME: -INTMIN is wrong!
*/
const char* uhub_itoa(int val)
{
size_t i;
int value;
static char buf[22];
memset(buf, 0, sizeof(buf));
if (!val)
{
strcat(buf, "0");
return buf;
}
i = sizeof(buf) - 1;
for (value = abs(val); value && i > 0; value /= 10)
buf[--i] = "0123456789"[value % 10];
if (val < 0 && i > 0)
buf[--i] = '-';
return buf+i;
}
const char* uhub_ulltoa(uint64_t val)
{
size_t i;
static char buf[22] = { 0, };
memset(buf, 0, sizeof(buf));
if (!val)
{
strcat(buf, "0");
return buf;
}
i = sizeof(buf) - 1;
for (; val && i > 0; val /= 10)
buf[--i] = "0123456789"[val % 10];
return buf+i;
}
#ifndef HAVE_STRNDUP
char* strndup(const char* string, size_t n)
{
size_t max = MIN(strlen(string), n);
char* tmp = hub_malloc(max+1);
memcpy(tmp, string, max);
tmp[max] = 0;
return tmp;
}
#endif
#ifndef HAVE_MEMMEM
void* memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen)
{
char* c_buf = (char*) haystack;
char* c_pat = (char*) needle;
char* ptr = memchr(c_buf, c_pat[0], haystacklen);
while (ptr && (&ptr[0] - &c_buf[0] < haystacklen))
{
if (!memcmp(ptr, c_pat, needlelen))
return ptr;
ptr = memchr(&ptr[1], c_pat[0], &c_buf[haystacklen] - &ptr[0]);
}
return 0;
}
#endif

58
src/misc.h Normal file
View File

@@ -0,0 +1,58 @@
/*
* 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_MISC_H
#define HAVE_UHUB_MISC_H
typedef int (*file_line_handler_t)(char* line, int line_number, void* data);
extern int is_num(char c);
extern int is_space(char c);
extern int is_white_space(char c);
extern int is_valid_utf8(const char* string);
extern int is_valid_base32_char(char c);
extern void base32_encode(const unsigned char* buffer, size_t len, char* result);
extern void base32_decode(const char* src, unsigned char* dst, size_t len);
extern char* strip_white_space(char* string);
extern int file_read_lines(const char* file, void* data, file_line_handler_t handler);
extern const char* uhub_itoa(int val);
extern const char* uhub_ulltoa(uint64_t val);
extern int uhub_atoi(const char* value);
#ifdef NEED_ATOLL
extern int atoll(const char* value);
#endif
#ifndef HAVE_STRNDUP
extern char* strndup(const char* string, size_t n);
#endif
#ifndef HAVE_MEMMEM
void* memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen);
#endif
#endif /* HAVE_UHUB_MISC_H */

292
src/netevent.c Normal file
View File

@@ -0,0 +1,292 @@
/*
* 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"
void net_on_read(int fd, short ev, void *arg)
{
static char buf[MAX_RECV_BUF];
struct user* user = (struct user*) arg;
char* pos;
char* start;
ssize_t offset;
ssize_t size;
ssize_t buflen;
int more = 1;
int flag_close = 0;
hub_log(log_trace, "net_on_read() : fd=%d, ev=%d, arg=%p", fd, (int) ev, arg);
if (ev == EV_TIMEOUT)
{
more = 0;
if (user_is_connecting(user))
{
flag_close = quit_timeout;
}
else
{
hub_send_ping(user);
}
}
while (more)
{
offset = 0;
if (user->recv_buf)
{
memcpy(buf, user->recv_buf, user->recv_buf_offset);
offset = user->recv_buf_offset;
}
else
{
offset = 0;
}
size = net_recv(fd, &buf[offset], MAX_RECV_BUF - offset, 0);
if (size == -1)
{
if (net_error() != EWOULDBLOCK)
flag_close = quit_socket_error;
break;
}
else if (size == 0)
{
flag_close = quit_disconnected;
break;
}
else
{
buflen = offset + size;
start = buf;
while ((pos = strchr(start, '\n')))
{
pos[0] = '\0';
if (strlen(start) > 0 && strlen(start) < user->hub->config->max_recv_buffer)
{
if (hub_handle_message(user, start, &pos[0]-&start[0]) == -1)
{
flag_close = quit_protocol_error;
more = 0;
break;
}
}
start = &pos[1];
}
if (!more) break;
if (&buf[offset + size] > &start[0])
{
if (!user->recv_buf)
{
user->recv_buf = hub_malloc(user->hub->config->max_recv_buffer);
}
if (!user->recv_buf)
{
flag_close = quit_memory_error;
break;
}
else
{
memcpy(user->recv_buf, start, &buf[offset + size] - &start[0]);
user->recv_buf_offset = &buf[offset + size] - &start[0];
}
}
else
{
if (user->recv_buf)
{
hub_free(user->recv_buf);
user->recv_buf = 0;
user->recv_buf_offset = 0;
}
}
}
}
if (flag_close)
{
user_disconnect(user, flag_close);
return;
}
if (user_is_logged_in(user))
{
if (user->ev_read)
{
struct timeval timeout = { TIMEOUT_IDLE, 0 };
event_add(user->ev_read, &timeout);
}
}
else if (user_is_connecting(user))
{
if (user->ev_read)
{
struct timeval timeout = { TIMEOUT_HANDSHAKE, 0 };
event_add(user->ev_read, &timeout);
}
}
}
void net_on_write(int fd, short ev, void *arg)
{
struct user* user = (struct user*) arg;
struct adc_message* msg;
int ret;
int length;
int close_flag = 0;
msg = list_get_first(user->send_queue);
while (msg)
{
length = msg->length - user->send_queue_offset;
ret = net_send(user->sd, &msg->cache[user->send_queue_offset], length, UHUB_SEND_SIGNAL);
if (ret == 0 || (ret == -1 && net_error() == EWOULDBLOCK))
{
close_flag = 0;
break;
}
else if (ret > 0)
{
user->tm_last_write = time(NULL);
if (ret == length)
{
user->send_queue_size -= ret;
user->send_queue_offset = 0;
list_remove(user->send_queue, msg);
if (user_flag_get(user, flag_user_list) && (msg == user->info || user->send_queue_size == 0))
{
user_flag_unset(user, flag_user_list);
}
adc_msg_free(msg);
msg = 0;
if (user->send_queue_size == 0)
break;
}
else
{
user->send_queue_size -= ret;
user->send_queue_offset -= ret;
break;
}
}
else
{
close_flag = quit_socket_error;
break;
}
msg = list_get_first(user->send_queue);
}
if (close_flag)
{
user_disconnect(user, close_flag);
}
else
{
if (user->send_queue_size > 0 && user->ev_write)
event_add(user->ev_write, NULL);
}
}
void net_on_accept(int server_fd, short ev, void *arg)
{
struct hub_info* hub = (struct hub_info*) arg;
struct user* user = 0;
int accept_more = 1;
const char* addr;
struct timeval timeout = { TIMEOUT_CONNECTED, 0 };
while (accept_more)
{
int fd = net_accept(server_fd);
if (fd == -1)
{
if (net_error() == EWOULDBLOCK)
{
break;
}
else
{
hub_log(log_error, "Accept error: %d %s", net_error(), strerror(net_error()));
break;
}
}
addr = net_get_peer_address(fd);
/* 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 */
ip_convert_to_binary(addr, &user->ipaddr);
net_set_nonblocking(fd, 1);
net_set_nosigpipe(fd, 1);
event_set(user->ev_read, fd, EV_READ | EV_PERSIST, net_on_read, user);
event_set(user->ev_write, fd, EV_WRITE, net_on_write, user);
event_add(user->ev_read, &timeout);
}
}
#ifdef ADC_UDP_OPERATION
extern void net_on_packet(int fd, short ev, void *arg)
{
static char buffer[1024] = {0,};
// struct hub_info* hub = (struct hub_info*) arg;
// struct user* user = 0;
ssize_t size;
struct sockaddr_storage from;
socklen_t fromlen;
size = recvfrom(fd, buffer, 1024, 0, (struct sockaddr*) &from, &fromlen);
// FIXME: A plugin should handle this!
hub_log(log_info, "Datagram [%s] (%d bytes)", buffer, (int) size);
}
#endif

53
src/netevent.h Normal file
View File

@@ -0,0 +1,53 @@
/*
* 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 for reading data from a socket.
*/
extern void net_on_read(int fd, short ev, void *arg);
/**
* Network callback for writing data to a socket.
*/
extern void net_on_write(int fd, short ev, void *arg);
/**
* Network callback for timers.
*/
extern void net_on_read_timeout(int fd, short ev, void* arg);
/**
* Network callback to accept incoming connections.
*/
extern void net_on_accept(int fd, short ev, void *arg);
#ifdef ADC_UDP_OPERATION
/**
* Network callback to receive incoming UDP datagram.
*/
extern void net_on_packet(int fd, short ev, void *arg);
#endif
#endif /* HAVE_UHUB_NET_EVENT_H */

326
src/network-epoll.c Normal file
View File

@@ -0,0 +1,326 @@
/*
* uhub - A tiny ADC p2p connection hub
* Copyright (C) 2007, 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 HAVE_EPOLL
// #define DEBUG_EPOLL
static struct epoll_event* events = 0;
static int epfd = -1;
#ifdef DEBUG_EPOLL
static void dump_listeners()
{
int i;
struct net_event_listener* listener;
hub_log(log_dump, "listeners: number=%d", num_connections);
for (i = 0; i < num_connections; i++)
{
listener = &listeners[i];
if (listener)
{
if (listener->fd != -1)
{
hub_log(log_dump, "epoll_dump_listeners: pos=%d/%d fd=%d, ptr=%p", i, num_connections, listeners->fd, listeners);
}
else
{
hub_log(log_dump, "epoll_dump_listeners: pos=%d/%d (unused)", i, num_connections);
}
listener = 0;
}
}
getc(stdin);
}
#endif
static void set_poll_events(struct epoll_event* handle, short trigger)
{
memset(handle, 0, sizeof(struct epoll_event));
if (trigger & evt_accept || trigger & evt_read || trigger & evt_close)
handle->events |= EPOLLIN;
if (trigger & evt_write)
handle->events |= EPOLLOUT;
if (trigger & evt_urgent)
handle->events |= EPOLLPRI;
#ifdef EPOLLRDHUP
if (triggers & evt_close)
handle->events |= EPOLLRDHUP;
#endif
}
static short get_poll_events(struct epoll_event* handle)
{
short trig = handle->events;
short evt = 0;
if (trig & EPOLLIN)
evt |= evt_read;
if (trig & EPOLLPRI)
evt |= evt_urgent;
if (trig & EPOLLOUT)
evt |= evt_write;
if (trig & EPOLLHUP)
evt |= evt_close;
if (trig & EPOLLERR)
evt |= evt_error;
#ifdef EPOLLRDHUP
if (trig & EPOLLRDHUP)
evt |= evt_close;
#endif
return evt;
}
int net_initialize(int capacity)
{
int i;
max_connections = capacity;
num_connections = 0;
epfd = epoll_create(max_connections);
if (epfd == -1)
{
hub_log(log_error, "net_initialize(): epoll_create failed");
return -1;
}
events = hub_malloc_zero(sizeof(struct epoll_event) * max_connections);
if (!events)
{
hub_log(log_error, "net_initialize(): hub_malloc failed");
return -1;
}
monitor_allocate((size_t) capacity);
#ifdef DEBUG_EPOLL
dump_listeners();
#endif
net_stats_initialize();
return 0;
}
int net_shutdown()
{
hub_log(log_trace, "Shutting down network monitor");
if (epfd != -1)
{
close(epfd);
}
hub_free(events);
hub_free(listeners);
return 0;
}
#ifdef DEBUG_EPOLL
uint64_t get_time_difference_in_msec(struct timeval before, struct timeval after)
{
uint64_t seconds = (after.tv_sec - before.tv_sec);
uint64_t out = seconds*1000;
if (seconds > 0)
out += ((after.tv_usec / 1000) + (1000 - (before.tv_usec / 1000)));
else
out += ((after.tv_usec - before.tv_usec) / 1000);
return out;
}
#endif
int net_wait(int timeout_ms)
{
int fired, n, max, ret;
struct net_event_listener* listener;
#ifdef DEBUG_EPOLL
struct timeval tm_before;
struct timeval tm_after;
gettimeofday(&tm_before, NULL);
dump_listeners();
#endif
fired = epoll_wait(epfd, events, num_connections, timeout_ms);
if (fired == -1) {
if (errno != EINTR)
{
hub_log(log_error, "net_wait(): epoll_wait failed");
}
return -1;
}
for (n = 0; n < fired; n++)
{
listener = (struct net_event_listener*) events[n].data.ptr;
listener->revents = get_poll_events(&events[n]);
hub_log(log_dump, "net_wait(): epoll event detected (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener);
}
max = num_connections;
for (n = 0; n < max; n++)
{
listener = &listeners[n];
if (listener && listener->fd != -1 && listener->revents)
{
hub_log(log_dump, "net_wait(): epoll trigger call (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener);
ret = listener->handler(listener);
listener->revents = 0;
}
#ifdef DEBUG_EPOLL
else
{
if (listener)
hub_log(log_dump, "net_wait(): epoll trigger ignore (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener);
}
#endif
}
#ifdef DEBUG_EPOLL
gettimeofday(&tm_after, NULL);
size_t diff = (size_t) get_time_difference_in_msec(tm_before, tm_after);
dump_listeners();
hub_log(log_debug, "net_wait(): time=%dms, triggered=%d", diff, fired);
#endif
return 0;
}
int net_add(int fd, short events, void* data, net_event_handler_t handler)
{
struct epoll_event ev;
struct net_event_listener* listener = monitor_get_free_listener();
hub_log(log_trace, "net_add(): adding socket (fd=%d, pos=%d)", fd, pos);
if (!listener)
{
hub_log(log_error, "net_add(): unable to poll more sockets");
return -1;
}
net_event_listener_set(listener, fd, events, data, handler);
set_poll_events(&ev, events);
ev.data.ptr = listener;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0)
{
hub_log(log_error, "net_add(): epoll_ctl error while adding socket (fd=%d)", fd);
net_event_listener_clear(listener);
return -1;
}
num_connections++;
#ifdef DEBUG_EPOLL
dump_listeners();
#endif
return 0;
}
int net_modify(int fd, short events)
{
struct epoll_event ev;
struct net_event_listener* listener = monitor_get_listener(fd);
hub_log(log_trace, "net_modify(): modifying socket events (fd=%d)", fd);
if (!listener)
{
hub_log(log_error, "net_modify(): unable to find socket.");
return -1;
}
listener->events = events;
set_poll_events(&ev, events);
ev.data.ptr = listener;
if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) < 0)
{
hub_log(log_error, "net_add(): epoll_ctl error while modifying socket (fd=%d)", fd);
return -1;
}
#ifdef DEBUG_EPOLL
dump_listeners();
#endif
return 0;
}
int net_remove(int fd)
{
struct epoll_event ev;
struct net_event_listener* listener = monitor_get_listener(fd);
hub_log(log_trace, "net_remove(): removing socket (fd=%d, pos=%d)", fd, pos);
if (!listener)
{
/* The socket is not being monitored */
hub_log(log_error, "net_remove(): unable to remove socket (fd=%d)", fd);
return -1;
}
net_event_listener_clear(listener);
if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev) < 0)
{
hub_log(log_error, "net_remove(): epoll_ctl error while removing socket (fd=%d)", fd);
return -1;
}
num_connections--;
#ifdef DEBUG_EPOLL
dump_listeners();
#endif
return 0;
}
#endif /* HAVE_EPOLL */

290
src/network-kqueue.c Normal file
View File

@@ -0,0 +1,290 @@
/*
* 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 HAVE_KQUEUE
static struct kevent* events = 0;
static struct kevent* change = 0;
static int kfd = -1;
static void set_poll_events(struct kevent* handle, short trigger)
{
if (!handle) {
hub_log(log_error, "OOOPS!!");
return;
}
memset(handle, 0, sizeof(struct kevent));
if (trigger & evt_accept || trigger & evt_read || trigger & evt_close)
handle->filter |= EVFILT_READ;
if (trigger & evt_write /* || trigger & evt_accept*/)
handle->filter |= EVFILT_WRITE;
}
static short get_poll_events(struct kevent* handle)
{
short trig = handle->flags;
short evt = 0;
if (trig & EVFILT_READ)
evt |= evt_read;
if (trig & EVFILT_WRITE)
evt |= evt_write;
if (trig & EV_EOF)
{
evt |= evt_close;
if (handle->fflags)
evt |= evt_error;
}
if (handle->filter == -1)
{
evt |= evt_error;
}
if (handle->data)
{
evt |= evt_accept;
}
if (evt)
{
hub_log(log_error, "Evt: fd=%d, filter=%d, flags=%d, fflags=%d, data=%d evt=%#x", handle->ident, handle->filter, handle->flags, handle->fflags, (int) handle->data, evt);
}
return evt;
}
int net_initialize(int capacity)
{
int i;
max_connections = capacity;
num_connections = 0;
kfd = kqueue();
if (kfd == -1)
{
hub_log(log_error, "net_initialize(): kqueue failed");
return -1;
}
events = (void*) hub_malloc_zero(sizeof(struct kevent) * max_connections);
if (!events)
{
hub_log(log_error, "net_initialize(): hub_malloc failed");
return -1;
}
change = (void*) hub_malloc_zero(sizeof(struct kevent) * max_connections);
if (!events)
{
hub_log(log_error, "net_initialize(): hub_malloc failed");
hub_free(events);
return -1;
}
listeners = (void*) hub_malloc_zero(sizeof(struct net_event_listener) * max_connections);
if (!listeners)
{
hub_log(log_error, "net_initialize(): hub_malloc failed");
hub_free(change);
hub_free(events);
return -1;
}
for (i = 0; i < max_connections; i++)
{
listeners[i].fd = -1;
}
net_stats_initialize();
return 0;
}
int net_shutdown()
{
if (kfd != -1) {
return close(kfd);
}
hub_free(events);
hub_free(change);
hub_free(listeners);
return 0;
}
int net_wait(int timeout_ms)
{
int fired, n, max, ret;
struct net_event_listener* listener;
struct timespec timeout = { (timeout_ms / 1000), (timeout_ms % 1000) * 1000 };
fired = kevent(kfd, events, num_connections, change, num_connections, &timeout);
if (fired == -1) {
if (errno != EINTR)
{
hub_log(log_error, "net_wait(): kevent failed");
}
return -1;
}
for (n = 0; n < fired; n++)
{
listener = (struct net_event_listener*) events[n].udata;
if (listener)
{
listener->revents = get_poll_events(&events[n]);
hub_log(log_dump, "net_wait(): kqueue event detected (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener);
}
}
max = num_connections;
for (n = 0; n < max; n++)
{
listener = &listeners[n];
if (listener && listener->fd != -1 && listener->revents != 0)
{
hub_log(log_dump, "net_wait(): kqueue trigger call (fd=%d, evt=%d, ptr=%p)", listener->fd, listener->revents, listener);
ret = listener->handler(listener);
listener->revents = 0;
}
}
return 0;
}
int net_add(int fd, short events_, void* data, net_event_handler_t handler)
{
struct kevent* event;
struct net_event_listener* listener = monitor_get_listener(fd);
hub_log(log_trace, "net_add(): adding socket (fd=%d)", fd);
if (listener)
{
/* Already added! */
return -1;
}
listener = monitor_get_free_listener();
if (!listener)
{
hub_log(log_error, "net_add(): unable to poll more sockets");
return -1;
}
net_event_listener_set(listener, fd, events_, data, handler);
event = &events[pos];
set_poll_events(event, events_);
event->ident = fd;
event->flags |= EV_ADD;
event->flags |= EV_ONESHOT;
#ifdef __APPLE__
event->flags |= EV_ENABLE;
#endif
event->udata = listener;
num_connections++;
return 0;
}
int net_modify(int fd, short events_)
{
struct kevent* event;
struct net_event_listener* listener = monitor_get_listener(fd);
hub_log(log_trace, "net_modify(): modifying socket (fd=%d)", fd);
if (!listener)
{
/* The socket is not being monitored */
hub_log(log_error, "net_modify(): unable to find socket (fd=%d)", fd);
return -1;
}
event = &events[pos];
// set_poll_events(event, events_);
event->ident = fd;
event->flags |= EV_ADD;
event->flags |= EV_ONESHOT;
#ifdef __APPLE__
event->flags |= EV_ENABLE;
#endif
return 0;
}
int net_remove(int fd)
{
struct kevent* event;
struct net_event_listener* listener = monitor_get_listener(fd);
hub_log(log_trace, "net_remove(): removing socket (fd=%d)", fd);
if (!listener)
{
/* The socket is not being monitored */
hub_log(log_error, "net_remove(): unable to remove socket (fd=%d)", fd);
return -1;
}
net_event_listener_clear(listener);
event = &events[pos];
event->ident = fd;
event->filter = 0;
event->flags = EV_DELETE;
#ifdef __APPLE__
event->flasg |= EV_DISABLE;
#endif
event->fflags = 0;
event->data = 0;
event->udata = 0;
num_connections--;
return 0;
}
#endif /* HAVE_KQUEUE */

656
src/network.c Normal file
View File

@@ -0,0 +1,656 @@
/*
* 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 is_ipv6_supported = -1; /* -1 = CHECK, 0 = NO, 1 = YES */
static int net_initialized = 0;
static struct net_statistics stats;
static struct net_statistics stats_total;
static struct event_base* evbase;
#if defined(IPV6_BINDV6ONLY)
#define SOCK_DUAL_STACK_OPT IPV6_BINDV6ONLY
#elif defined(IPV6_V6ONLY)
#define SOCK_DUAL_STACK_OPT IPV6_V6ONLY
#endif
int net_initialize()
{
if (!net_initialized)
{
#ifdef WINSOCK
struct WSAData wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != NO_ERROR)
{
hub_log(log_error, "Unable to initialize winsock.");
return -1;
}
#endif /* WINSOCK */
hub_log(log_trace, "Initializing network monitor.");
net_stats_initialize();
#ifdef SSL_SUPPORT
/* FIXME: Initialize OpenSSL here. */
#endif /* SSL_SUPPORT */
#ifdef OLD_LIBEVENT
event_init();
#else
evbase = event_init();
if (!evbase)
{
hub_log(log_error, "Unable to initialize libevent.");
return -1;
}
#endif
net_initialized = 1;
return 0;
}
return -1;
}
int net_shutdown()
{
if (net_initialized)
{
hub_log(log_trace, "Shutting down network monitor");
#ifdef SSL_SUPPORT
/* FIXME: Shutdown OpenSSL here. */
#endif
#ifndef OLD_LIBEVENT
event_base_free(evbase);
#endif
evbase = 0;
#ifdef WINSOCK
WSACleanup();
#endif
net_initialized = 0;
return 0;
}
return -1;
}
int net_error()
{
#ifdef WINSOCK
return WSAGetLastError();
#else
return errno;
#endif
}
const char* net_error_string(int code)
{
#ifdef WINSOCK
static char string[32];
snprintf(string, 32, "error code: %d", code);
return string;
#else
return strerror(code);
#endif
}
static int net_setsockopt(int fd, int level, int opt, const void* optval, socklen_t optlen)
{
#ifdef WINSOCK
return setsockopt(fd, level, opt, (const char*) optval, optlen);
#else
return setsockopt(fd, level, opt, optval, optlen);
#endif
}
int net_set_nonblocking(int fd, int toggle)
{
int ret;
#ifdef NETAPI_DUMP
hub_log(log_dump, "net_set_nonblocking(): fd=%d", fd);
#endif
#ifdef WINSOCK
u_long on = toggle ? 1 : 0;
ret = ioctlsocket(fd, FIONBIO, &on);
#else
ret = ioctl(fd, FIONBIO, &toggle);
#endif
if (ret == -1)
{
hub_log(log_error, "net_set_nonblocking(): ioctl failed (fd=%d): %s", fd, net_error_string(net_error()));
return -1;
}
return 0;
}
/* NOTE: Possibly only supported on BSD and OSX? */
int net_set_nosigpipe(int fd, int toggle)
{
#ifdef SO_NOSIGPIPE
int ret;
#ifdef NETAPI_DUMP
hub_log(log_dump, "net_set_nosigpipe(): fd=%d", fd);
#endif
ret = net_setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &toggle, sizeof(toggle));
if (ret == -1)
{
hub_log(log_error, "net_set_linger(): setsockopt failed (fd=%d): %s", fd, net_error_string(net_error()));
return -1;
}
#endif
return 0;
}
int net_set_close_on_exec(int fd, int toggle)
{
#ifdef NETAPI_DUMP
hub_log(log_dump, "net_set_close_on_exec(): fd=%d", fd);
#endif
#ifdef WINSOCK
return -1; /* FIXME: How is this done on Windows? */
#else
return fcntl(fd, F_SETFD, toggle);
#endif
}
int net_set_linger(int fd, int toggle)
{
int ret;
#ifdef NETAPI_DUMP
hub_log(log_dump, "net_set_linger(): fd=%d", fd);
#endif
ret = net_setsockopt(fd, SOL_SOCKET, SO_LINGER, &toggle, sizeof(toggle));
if (ret == -1)
{
hub_log(log_error, "net_set_linger(): setsockopt failed (fd=%d): %s", fd, net_error_string(net_error()));
return -1;
}
return 0;
}
int net_set_keepalive(int fd, int toggle)
{
int ret;
#ifdef NETAPI_DUMP
hub_log(log_dump, "net_set_keepalive(): fd=%d", fd);
#endif
ret = net_setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &toggle, sizeof(toggle));
if (ret == -1)
{
hub_log(log_error, "net_set_keepalive(): setsockopt failed (fd=%d): %s", fd, net_error_string(net_error()));
return -1;
}
return 0;
}
int net_set_reuseaddress(int fd, int toggle)
{
int ret;
#ifdef NETAPI_DUMP
hub_log(log_dump, "net_set_reuseaddress(): fd=%d", fd);
#endif
ret = net_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &toggle, sizeof(toggle));
if (ret == -1)
{
hub_log(log_error, "net_set_reuseaddress(): setsockopt failed (fd=%d): %s", fd, net_error_string(net_error()));
return -1;
}
return 0;
}
int net_close(int fd)
{
#ifdef WINSOCK
int ret = closesocket(fd);
#else
int ret = close(fd);
#endif
if (ret == 0)
{
net_stats_add_close();
}
else
{
if (fd != -1)
{
net_stats_add_error();
}
}
return ret;
}
int net_accept(int fd)
{
struct sockaddr_storage addr;
socklen_t addr_size;
int ret = 0;
addr_size = sizeof(struct sockaddr_storage);
memset(&addr, 0, addr_size);
ret = accept(fd, (struct sockaddr*) &addr, &addr_size);
if (ret == -1)
{
switch (net_error())
{
#if defined(__linux__)
case ENETDOWN:
case EPROTO:
case ENOPROTOOPT:
case EHOSTDOWN:
case ENONET:
case EHOSTUNREACH:
case EOPNOTSUPP:
errno = EWOULDBLOCK;
#endif
case EWOULDBLOCK:
break;
default:
hub_log(log_error, "net_accept(): accept failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error()));
net_stats_add_error();
return -1;
}
}
else
{
net_stats_add_accept();
}
return ret;
}
int net_connect(int fd, const struct sockaddr *serv_addr, socklen_t addrlen)
{
int ret = connect(fd, serv_addr, addrlen);
if (ret == -1)
{
if (net_error() != EINPROGRESS)
{
hub_log(log_error, "net_connect(): connect failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error()));
net_stats_add_error();
}
}
return ret;
}
int net_is_ipv6_supported()
{
if (is_ipv6_supported == -1)
{
int ret = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (ret == -1)
{
#ifdef WINSOCK
if (net_error() == WSAEAFNOSUPPORT)
#else
if (net_error() == EAFNOSUPPORT)
#endif
{
hub_log(log_trace, "net_is_ipv6_supported(): IPv6 is not supported on this system.");
is_ipv6_supported = 0;
return 0;
}
hub_log(log_error, "net_is_ipv6_supported(): Unknown error (errno=%d, msg=%s)", net_error(), net_error_string(net_error()));
}
else
{
#ifdef SOCK_DUAL_STACK_OPT
int off = 0;
if (net_setsockopt(ret, IPPROTO_IPV6, SOCK_DUAL_STACK_OPT, (char*) &off, sizeof(off)) < 0)
{
hub_log(log_error, "net_socket_create(): Dual stack IPv6/IPv4 is not supported.");
is_ipv6_supported = 0;
}
else
{
is_ipv6_supported = 1;
}
#else
is_ipv6_supported = 0;
#endif
net_close(ret);
}
}
return is_ipv6_supported;
}
int net_socket_create(int af, int type, int protocol)
{
int sd = socket(af, type, protocol);
if (sd == -1)
{
hub_log(log_error, "net_socket_create(): socket failed (errno=%d, msg=%s)", net_error(), net_error_string(net_error()));
}
#ifdef SOCK_DUAL_STACK_OPT
/* BSD style */
if (af == AF_INET6)
{
int off = 0;
if (net_setsockopt(sd, IPPROTO_IPV6, SOCK_DUAL_STACK_OPT, (char*) &off, sizeof(off)) < 0)
{
hub_log(log_error, "net_socket_create(): Cannot set socket to dual stack mode IPv6/IPv4 (%d - %s).", net_error(), net_error_string(net_error()));
}
}
#endif
return sd;
}
const char* net_address_to_string(int af, const void* src, char* dst, socklen_t cnt)
{
#ifdef WINSOCK
struct sockaddr_in sin4;
struct sockaddr_in6 sin6;
struct in_addr* addr4 = (struct in_addr*) src;
struct in6_addr* addr6 = (struct in6_addr*) src;
size_t size;
LPSOCKADDR addr;
DWORD len = cnt;
switch (af)
{
case AF_INET:
sin4.sin_family = AF_INET;
sin4.sin_port = 0;
sin4.sin_addr = *addr4;
size = sizeof(sin4);
addr = (LPSOCKADDR) &sin4;
break;
case AF_INET6:
sin6.sin6_family = AF_INET6;
sin6.sin6_port = 0;
sin6.sin6_addr = *addr6;
size = sizeof(sin6);
addr = (LPSOCKADDR) &sin6;
break;
default:
return NULL;
}
if (WSAAddressToString(addr, size, NULL, dst, &len) == 0)
{
return dst;
}
return NULL;
#else
return inet_ntop(af, src, dst, cnt);
#endif
}
int net_string_to_address(int af, const char* src, void* dst)
{
#ifdef WINSOCK
int ret, size;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
struct sockaddr* addr = 0;
if (af == AF_INET6)
{
if (net_is_ipv6_supported() != 1) return -1;
size = sizeof(struct sockaddr_in6);
addr = (struct sockaddr*) &addr6;
}
else
{
size = sizeof(struct sockaddr_in);
addr = (struct sockaddr*) &addr4;
}
if (!net_initialized)
net_initialize();
ret = WSAStringToAddressA((char*) src, af, NULL, addr, &size);
if (ret == -1)
{
return -1;
}
if (af == AF_INET6)
{
memcpy(dst, &addr6.sin6_addr, sizeof(addr6.sin6_addr));
}
else
{
memcpy(dst, &addr4.sin_addr, sizeof(addr4.sin_addr));
}
return 1;
#else
return inet_pton(af, src, dst);
#endif
}
const char* net_get_peer_address(int fd)
{
static char address[INET6_ADDRSTRLEN+1];
struct sockaddr_storage storage;
struct sockaddr_in6* name6;
struct sockaddr_in* name4;
struct sockaddr* name;
memset(address, 0, INET6_ADDRSTRLEN);
socklen_t namelen = sizeof(struct sockaddr_storage);
memset(&storage, 0, namelen);
name6 = (struct sockaddr_in6*) &storage;
name4 = (struct sockaddr_in*) &storage;
name = (struct sockaddr*) &storage;
int af = net_is_ipv6_supported() ? AF_INET6 : AF_INET;
if (getpeername(fd, (struct sockaddr*) name, &namelen) != -1)
{
if (af == AF_INET6)
{
net_address_to_string(af, (void*) &name6->sin6_addr, address, INET6_ADDRSTRLEN);
if (strncmp(address, "::ffff:", 7) == 0) /* IPv6 mapped IPv4 address. */
{
return &address[7];
}
hub_log(log_trace, "net_get_peer_address(): address=%s", address);
return address;
}
else
{
net_address_to_string(af, (void*) &name4->sin_addr, address, INET6_ADDRSTRLEN);
hub_log(log_trace, "net_get_peer_address(): address=%s", address);
return address;
}
}
else
{
hub_log(log_error, "net_get_peer_address(): getsockname failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error()));
}
return "0.0.0.0";
}
ssize_t net_recv(int fd, void* buf, size_t len, int flags)
{
ssize_t ret = recv(fd, buf, len, flags);
if (ret >= 0)
{
net_stats_add_rx(ret);
}
else
{
if (net_error() != EWOULDBLOCK)
{
hub_log(log_debug, "net_recv(): failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error()));
net_stats_add_error();
}
}
return ret;
}
ssize_t net_send(int fd, void* buf, size_t len, int flags)
{
ssize_t ret = send(fd, buf, len, flags);
if (ret >= 0)
{
net_stats_add_tx(ret);
}
else
{
if (net_error() != EWOULDBLOCK)
{
hub_log(log_debug, "net_send(): failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error()));
net_stats_add_error();
}
}
return ret;
}
int net_bind(int fd, const struct sockaddr *my_addr, socklen_t addrlen)
{
int ret = bind(fd, my_addr, addrlen);
if (ret == -1)
{
hub_log(log_error, "net_bind(): failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error()));
net_stats_add_error();
}
return ret;
}
int net_listen(int fd, int backlog)
{
int ret = listen(fd, backlog);
if (ret == -1)
{
hub_log(log_error, "net_listen(): failed (fd=%d, errno=%d, msg=%s)", fd, net_error(), net_error_string(net_error()));
net_stats_add_error();
}
return ret;
}
void net_stats_initialize()
{
memset(&stats_total, 0, sizeof(struct net_statistics));
stats_total.timestamp = time(NULL);
memset(&stats, 0, sizeof(struct net_statistics));
stats.timestamp = time(NULL);
}
void net_stats_get(struct net_statistics** intermediate, struct net_statistics** total)
{
*intermediate = &stats;
*total = &stats_total;
}
void net_stats_report()
{
int factor = (time(NULL) - stats.timestamp);
if (!factor) factor++;
hub_log(log_info, "Statistics NET: tx=%d KB/s, rx=%d KB/s, (acc=%d/cls=%d/err=%d)",
(int) ((stats.tx / factor) / 1024),
(int) ((stats.rx / factor) / 1024),
(int) stats.accept,
(int) stats.closed,
(int) stats.errors);
}
void net_stats_reset()
{
stats_total.tx += stats.tx;
stats_total.rx += stats.rx;
stats_total.accept += stats.accept;
stats_total.errors += stats.errors;
stats_total.closed += stats.closed;
memset(&stats, 0, sizeof(struct net_statistics));
stats.timestamp = time(NULL);
}
int net_stats_timeout()
{
/* FIXME: Configurable time for dumping network statistics */
return (time(NULL) - stats.timestamp > 60) ? 1 : 0;
}
void net_stats_add_tx(size_t bytes)
{
stats.tx += bytes;
}
void net_stats_add_rx(size_t bytes)
{
stats.rx += bytes;
}
void net_stats_add_accept()
{
stats.accept++;
}
void net_stats_add_error()
{
stats.errors++;
}
void net_stats_add_close()
{
stats.closed++;
}

256
src/network.h Normal file
View File

@@ -0,0 +1,256 @@
/*
* 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_NETWORK_H
#define HAVE_UHUB_NETWORK_H
struct net_statistics
{
time_t timestamp;
size_t tx;
size_t rx;
size_t accept;
size_t closed;
size_t errors;
};
/**
* Initialize the socket monitor subsystem.
* On some operating systems this will also involve loading the TCP/IP stack
* (needed on Windows at least).
*
* @param max_connections The maximum number of sockets the monitor can handle.
* @return -1 on error, 0 on success
*/
extern int net_initialize();
/**
* Shutdown the socket monitor.
* On some operating systems this will also ensure the TCP/IP stack
* is loaded.
*
* @return -1 on error, 0 on success
*/
extern int net_shutdown();
/**
* @return the number of sockets currrently being monitored.
*/
extern int net_monitor_count();
/**
* @return the monitor's socket capacity.
*/
extern int net_monitor_capacity();
/**
* @return the last error code occured.
*
* NOTE: On Windows this is the last error code from the socket library, but
* on UNIX this is the errno variable that can be overwritten by any
* libc function.
* For this reason, only rely on net_error() immediately after a
* socket function call.
*/
extern int net_error();
extern const char* net_error_string(int code);
/**
* A wrapper for the socket() function call.
*/
extern int net_socket_create(int af, int type, int protocol);
/**
* A wrapper for the close() function call.
*/
extern int net_close(int fd);
/**
* A wrapper for the accept() function call.
*/
extern int net_accept(int fd);
/**
* A wrapper for the connect() call.
*/
extern int net_connect(int fd, const struct sockaddr *serv_addr, socklen_t addrlen);
/**
* A wrapper for the bind() function call.
*/
extern int net_bind(int fd, const struct sockaddr *my_addr, socklen_t addrlen);
/**
* A wrapper for the listen() function call.
*/
extern int net_listen(int sockfd, int backlog);
/**
* This will set the socket to blocking or nonblocking mode.
* @param fd socket descriptor
* @param toggle if non-zero nonblocking mode, otherwise blocking mode is assumed
* @return -1 on error, 0 on success
*/
extern int net_set_nonblocking(int fd, int toggle);
/**
* This will prevent the socket to generate a SIGPIPE in case the socket goes down.
* NOTE: Not all operating systems support this feature. In that case this will return success value.
*
* @param fd socket descriptor
* @param toggle if non-zero ignore sigpipe, otherwise disable it.
* @return -1 on error, 0 on success
*/
extern int net_set_nosigpipe(int fd, int toggle);
/**
* This will set the close-on-exec flag. This means if any subprocess is
* started any open file descriptors or sockets will not be inherited if this
* is turned on. Otherwise, subprocesses invoked via exec() can read/write
* to these sockets.
*
* @param fd socket descriptor
* @param toggle if non-zero close-on-exec is enabled, otherwise disabled.
* @return -1 on error, 0 on success.
*/
extern int net_set_close_on_exec(int fd, int toggle);
/**
* Enable/disable linger on close if data is present.
*
* @param fd socket descriptor
* @param toggle enable if non-zero
* @return -1 on error, 0 on success.
*/
extern int net_set_linger(int fd, int toggle);
/**
* This will set or unset the SO_REUSEADDR flag.
* @param fd socket descriptor
* @param toggle Set SO_REUSEADDR if non-zero, otherwise unset it.
* @return -1 on error, 0 on success
*/
extern int net_set_reuseaddress(int fd, int toggle);
/**
* A wrapper for the recv() function call.
*/
extern ssize_t net_recv(int fd, void* buf, size_t len, int flags);
/**
* A wrapper for the send() function call.
*/
extern ssize_t net_send(int fd, void* buf, size_t len, int flags);
/**
* This tries to create a AF_INET6 socket.
* If it succeeds it concludes IPv6 is supported on the host operating
* system. If the call fails with EAFNOSUPPORT the host system
* does not support IPv6.
* The result is cached so further calls to this function are cheap.
*/
extern int net_is_ipv6_supported();
/**
* This will return a string containing the peer IP-address of
* the connected peer associated with the given socket.
*
* @param fd socket descriptor
* @return IP address (IPv6 or IPv4), or "0.0.0.0" if unable to determine the address.
*/
extern const char* net_get_peer_address(int fd);
/**
* See man(3) inet_ntop.
*/
extern const char* net_address_to_string(int af, const void *src, char *dst, socklen_t cnt);
/**
* See man(3) inet_pton.
*/
extern int net_string_to_address(int af, const char *src, void *dst);
/**
* Network statistics monitor.
*
* Keeps track of bandwidth usage, sockets accepted, closed,
* errors etc.
*/
extern void net_stats_initialize();
extern void net_stats_report();
extern void net_stats_reset();
extern void net_stats_add_tx(size_t bytes);
extern void net_stats_add_rx(size_t bytes);
extern void net_stats_add_accept();
extern void net_stats_add_error();
extern void net_stats_add_close();
extern int net_stats_timeout();
extern void net_stats_get(struct net_statistics** intermediate, struct net_statistics** total);
#if defined(WINSOCK) && !defined(__CYGWIN__)
// #define EINTR WSAEINTR
// #define EACCES WSAEACCES
// #define EFAULT WSAEFAULT
// #define EINVAL WSAEINVAL
// #define EMFILE WSAEMFILE
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEINPROGRESS
#define EALREADY WSAEALREADY
#define ENOTSOCK WSAENOTSOCK
#define EDESTADDRREQ WSAEDESTADDRREQ
#define EMSGSIZE WSAEMSGSIZE
#define EPROTOTYPE WSAEPROTOTYPE
#define ENOPROTOOPT WSAENOPROTOOPT
#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
#define EOPNOTSUPP WSAEOPNOTSUPP
#define EPFNOSUPPORT WSAEPFNOSUPPORT
#define EAFNOSUPPORT WSAEAFNOSUPPORT
#define EADDRINUSE WSAEADDRINUSE
#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
#define ENETDOWN WSAENETDOWN
#define ENETUNREACH WSAENETUNREACH
#define ENETRESET WSAENETRESET
#define ECONNABORTED WSAECONNABORTED
#define ECONNRESET WSAECONNRESET
#define ENOBUFS WSAENOBUFS
#define EISCONN WSAEISCONN
#define ENOTCONN WSAENOTCONN
#define ESHUTDOWN WSAESHUTDOWN
#define ETOOMANYREFS WSAETOOMANYREFS
#define ETIMEDOUT WSAETIMEDOUT
#define ECONNREFUSED WSAECONNREFUSED
#define ELOOP WSAELOOP
// #define ENAMETOOLONG WSAENAMETOOLONG
#define EHOSTDOWN WSAEHOSTDOWN
#define EHOSTUNREACH WSAEHOSTUNREACH
// #define ENOTEMPTY WSAENOTEMPTY
#define EPROCLIM WSAEPROCLIM
#define EUSERS WSAEUSERS
#define EDQUOT WSAEDQUOT
#define ESTALE WSAESTALE
#define EREMOTE WSAEREMOTE
#endif /* WINSOCK && !__CYGWIN__ */
#endif /* HAVE_UHUB_NETWORK_H */

52
src/plugin.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* 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/>.
*
*/
typedef void (*plugin_event_startup)(struct hub*);
typedef void (*plugin_event_shutdown)(struct hub*);
typedef void (*plugin_event_user_login)(struct hub*, struct user*);
typedef void (*plugin_event_user_logout)(struct hub*, struct user*);
typedef int (*plugin_event_connect)(struct hub*, struct ip_addr_encap);
typedef void (*plugin_event_disconnect)(struct hub*, struct user*);
typedef int (*plugin_event_message)(struct hub*, struct user*, struct adc_message*);
typedef void (*plugin_event_support)(struct hub*, struct user*, int);
struct uhub_plugin
{
/** Starting the hub */
plugin_event_startup evt_startup;
/** Shutting down the hub */
plugin_event_shutdown evt_shutdown;
/** Someone connected to the hub (we only have IP at this point). */
plugin_event_connect evt_connect;
/** Someone disconnected from the hub (but was not successfully logged in). */
plugin_event_disconnect evt_disconnect;
/** A client sent a message about which protocol extensions it supports */
plugin_event_support evt_support;
/** A client was successfully logged in to the hub */
plugin_event_user_login evt_login;
/** A client (previously logged in) has disconnected. */
plugin_event_user_logout evt_logout;
};

237
src/route.c Normal file
View File

@@ -0,0 +1,237 @@
/*
* 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 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(u->hub, msg);
break;
case 'D':
target = get_user_by_sid(u->hub, msg->target);
if (target)
{
route_to_user(target, msg);
}
break;
case 'E':
target = get_user_by_sid(u->hub, msg->target);
if (target)
{
route_to_user(target, msg);
route_to_user(u, msg);
}
break;
case 'F':
route_to_subscribers(u->hub, msg);
break;
default:
/* Ignore the message */
break;
}
return 0;
}
static void queue_command(struct user* user, struct adc_message* msg__, int offset)
{
struct adc_message* msg = adc_msg_incref(msg__);
list_append(user->send_queue, msg);
hub_log(log_trace, "queue_command(), user=%p, msg=%p (%zu), offset=%d", user, msg, msg->references, offset);
if (offset > 0)
{
user->send_queue_size += msg->length - offset;
user->send_queue_offset = offset;
user->tm_last_write = time(NULL);
}
else
{
user->send_queue_size += msg->length;
user->send_queue_offset = 0;
}
}
// #define ALWAYS_QUEUE_MESSAGES
int route_to_user(struct user* user, struct adc_message* msg)
{
int ret;
#if LOG_SEND_MESSAGES_WHEN_ROUTED
char* data = strndup(msg->cache, msg->length-1);
hub_log(log_protocol, "send %s: %s", sid_to_string(user->sid), data);
free(data);
#endif
#ifndef ALWAYS_QUEUE_MESSAGES
if (user->send_queue_size == 0 && !user_is_disconnecting(user))
{
ret = net_send(user->sd, msg->cache, msg->length, UHUB_SEND_SIGNAL);
if (ret == msg->length)
{
return 1;
}
if (ret >= 0 || (ret == -1 && net_error() == EWOULDBLOCK))
{
queue_command(user, msg, ret);
if (user->send_queue_size && user->ev_write)
event_add(user->ev_write, NULL);
}
else
{
/* A socket error occured */
user_disconnect(user, quit_socket_error);
return 0;
}
}
else
#endif
{
if (!user_flag_get(user, flag_user_list) && user->send_queue_size + msg->length > user->hub->config->max_send_buffer && msg->priority >= 0)
{
/* User is not able to swallow the data, let's cut our losses and disconnect. */
user_disconnect(user, quit_send_queue);
return 0;
}
else
{
if (user->send_queue_size + msg->length > user->hub->config->max_send_buffer_soft && msg->priority >= 0)
{
/* Don't queue this message if it is low priority! */
}
else
{
queue_command(user, msg, 0);
if (user->ev_write)
event_add(user->ev_write, NULL);
}
}
}
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(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(user, command);
}
}
user = (struct user*) list_get_next(hub->users->list);
}
return 0;
}
int route_info_message(struct user* u)
{
if (!user_is_nat_override(u))
{
return route_to_all(u->hub, u->info);
}
else
{
struct adc_message* cmd = adc_msg_copy(u->info);
const char* address = (char*) net_get_peer_address(u->sd);
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(u->hub->users->list);
while (user)
{
if (user_is_nat_override(user))
route_to_user(user, cmd);
else
route_to_user(user, u->info);
user = (struct user*) list_get_next(u->hub->users->list);
}
adc_msg_free(cmd);
}
return 0;
}

51
src/route.h Normal file
View File

@@ -0,0 +1,51 @@
/*
* 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 user* u, struct adc_message* msg);
/**
* Transmit message directly to one user.
*/
extern int route_to_user(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 user* user);
#endif /* HAVE_UHUB_ROUTE_H */

61
src/sid.c Normal file
View File

@@ -0,0 +1,61 @@
/*
* 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"
const char* BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
char* sid_to_string(sid_t sid_)
{
static char t_sid[5];
sid_t sid = (sid_ & 0xFFFFF); /* 20 bits only */
sid_t A, B, C, D = 0;
D = (sid % 32);
sid = (sid - D) / 32;
C = (sid % 32);
sid = (sid - C) / 32;
B = (sid % 32);
sid = (sid - B) / 32;
A = (sid % 32);
t_sid[0] = BASE32_ALPHABET[A];
t_sid[1] = BASE32_ALPHABET[B];
t_sid[2] = BASE32_ALPHABET[C];
t_sid[3] = BASE32_ALPHABET[D];
t_sid[4] = 0;
return t_sid;
}
sid_t string_to_sid(const char* sid)
{
sid_t nsid = 0;
sid_t n, x;
sid_t factors[] = { 32768, 1024, 32, 1};
if (!sid || strlen(sid) != 4) return 0;
for (n = 0; n < 4; n++) {
for (x = 0; x < strlen(BASE32_ALPHABET); x++)
if (sid[n] == BASE32_ALPHABET[x]) break;
if (x == 32) return 0;
nsid += x * factors[n];
}
return nsid;
}

67
src/sid.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* 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_SID_H
#define HAVE_UHUB_SID_H
#define SID_MAX 1048576
extern const char* BASE32_ALPHABET;
extern char* sid_to_string(sid_t sid_);
extern sid_t string_to_sid(const char* sid);
struct sid_map
{
struct user* ptr;
struct sid_map* next;
};
/**
* Session IDs are heavily reused, since they are a fairly scarce
* resource. Only one (2^10)-1 exist, since it is a four byte base32-encoded
* value and 'AAAA' (0) is reserved for the hub.
*
* Initialize with sid_initialize(), which sets min and max to one, and count to 0.
*
* When allocating a session ID:
* - If 'count' is less than the pool size (max-min), then allocate within the pool
* - Increase the pool size (see below)
* - If unable to do that, hub is really full - don't let anyone in!
*
* When freeing a session ID:
* - If the session ID being freed is 'max', then decrease the pool size by one.
*
*/
struct sid_pool
{
sid_t min;
sid_t max;
sid_t count;
struct sid_map* map;
};
extern void sid_initialize(struct sid_pool*);
extern sid_t sid_alloc(struct sid_pool*, struct user*);
extern void sid_free(struct sid_pool*, sid_t);
#endif /* HAVE_UHUB_SID_H */

1241
src/tiger.c Normal file

File diff suppressed because it is too large Load Diff

26
src/tiger.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* 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_HASH_TIGER_H
#define HAVE_UHUB_HASH_TIGER_H
extern void tiger(uint64_t* str, uint64_t length, uint64_t* res);
#endif /* HAVE_UHUB_HASH_TIGER_H */

168
src/uhub.h Normal file
View File

@@ -0,0 +1,168 @@
/*
* 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_COMMON_H
#define HAVE_UHUB_COMMON_H
/* Debugging */
/* #define NETWORK_DUMP_DEBUG */
/* #define MEMORY_DEBUG */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#if defined(__CYGWIN__) || defined(__MINGW32__)
#ifndef WINSOCK
#define WINSOCK
#endif
#endif
#ifdef WINSOCK
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#endif
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifndef WIN32
#include <grp.h>
#include <pwd.h>
#define HAVE_STRNDUP
#define HAVE_MEMMEM
#endif
#ifdef SSL_SUPPORT
#include <openssl/ssl.h>
#endif
#include "../version.h"
#define uhub_assert assert
#include <event.h>
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
#undef HAVE_STRNDUP
#undef HAVE_MEMMEM
#endif
#ifdef MSG_NOSIGNAL
#define UHUB_SEND_SIGNAL MSG_NOSIGNAL
#else
#ifdef MSG_NOPIPE
#define UHUB_SEND_SIGNAL MSG_NOPIPE
#else
#define UHUB_SEND_SIGNAL 0
#endif
#endif
#define SERVER_PORT 1511
#define SERVER_ADDR_IPV6 "::"
#define SERVER_ADDR_IPV4 "0.0.0.0"
#define SERVER_BACKLOG 50
#ifndef WIN32
#define SERVER_CONFIG "/etc/uhub/uhub.conf"
#define SERVER_ACL_FILE "/etc/uhub/users.conf"
#else
#define SERVER_CONFIG "uhub.conf"
#define SERVER_ACL_FILE "users.conf"
#ifndef stderr
#define stderr stdout
#endif
#endif
#define TIMEOUT_CONNECTED 15
#define TIMEOUT_HANDSHAKE 30
#define TIMEOUT_SENDQ 120
#define TIMEOUT_IDLE 7200
#define TIMEOUT_STATS 3600
#define MAX_CLIENTS 512
#define MAX_CID_LEN 39
#define MAX_NICK_LEN 64
#define MAX_UA_LEN 32
#define TIGERSIZE 24
#define MAX_RECV_BUF 65535
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 46
#endif
#include "adcconst.h"
#define MIN(a, b) (a < b ? a : b)
#define MAX(a, b) (a > b ? a : b)
#ifdef __cplusplus
extern "C" {
#endif
#include "memory.h"
#include "misc.h"
#include "eventid.h"
#include "eventqueue.h"
#include "ipcalc.h"
#include "list.h"
#include "sid.h"
#include "network.h"
#include "netevent.h"
#include "auth.h"
#include "tiger.h"
#include "config.h"
#include "log.h"
#include "user.h"
#include "usermanager.h"
#include "message.h"
#include "route.h"
#include "hub.h"
#include "commands.h"
#include "inf.h"
#include "hubevent.h"
#ifdef __cplusplus
}
#endif
#endif /* HAVE_UHUB_COMMON_H */

308
src/user.c Normal file
View File

@@ -0,0 +1,308 @@
/*
* 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 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->ev_write = hub_malloc_zero(sizeof(struct event));
user->ev_read = hub_malloc_zero(sizeof(struct event));
if (!user->ev_write || !user->ev_read)
{
hub_free(user->ev_read);
hub_free(user->ev_write);
hub_free(user);
return NULL;
}
user->sd = sd;
user->tm_connected = time(NULL);
user->hub = hub;
user->feature_cast = 0;
user->send_queue = list_create();
user->send_queue_offset = 0;
user->send_queue_size = 0;
user->recv_buf_offset = 0;
user->recv_buf = 0;
user_set_state(user, state_protocol);
return user;
}
static void clear_send_queue_callback(void* ptr)
{
adc_msg_free((struct adc_message*) ptr);
}
void user_destroy(struct user* user)
{
hub_log(log_trace, "user_destroy(), user=%p", user);
if (user->ev_write)
{
event_del(user->ev_write);
hub_free(user->ev_write);
user->ev_write = 0;
}
if (user->ev_read)
{
event_del(user->ev_read);
hub_free(user->ev_read);
user->ev_read = 0;
}
net_close(user->sd);
adc_msg_free(user->info);
user_clear_feature_cast_support(user);
if (user->recv_buf)
{
hub_free(user->recv_buf);
}
if (user->send_queue)
{
list_clear(user->send_queue, &clear_send_queue_callback);
list_destroy(user->send_queue);
}
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))
{
puts("PANIC - Ignoring new state");
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);
}
static int convert_support_fourcc(int fourcc)
{
switch (fourcc)
{
case FOURCC('B','A','S','0'): /* Obsolete */
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_schedule_destroy(struct user* user)
{
struct event_data post;
memset(&post, 0, sizeof(post));
post.id = UHUB_EVENT_USER_DESTROY;
post.ptr = user;
event_queue_post(user->hub->queue, &post);
}
void user_disconnect(struct user* user, int reason)
{
struct event_data post;
int need_notify = 0;
if (user_is_disconnecting(user))
{
return;
}
/* dont read more data from this user */
if (user->ev_read)
{
event_del(user->ev_read);
hub_free(user->ev_read);
user->ev_read = 0;
}
hub_log(log_trace, "user_disconnect(), 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(user->hub->queue, &post);
}
else
{
user->quit_reason = quit_unknown;
user_schedule_destroy(user);
}
}
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_disconnecting(struct user* user)
{
if (user->state == state_cleanup || user->state == state_disconnected)
return 1;
return 0;
}

261
src/user.h Normal file
View File

@@ -0,0 +1,261 @@
/*
* 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;
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_choke = 0x02000000, /** Choked: Cannot send, waiting for write event */
flag_want_read = 0x04000000, /** Need to read (SSL) */
flag_want_write = 0x08000000, /** Need to write (SSL) */
flag_user_list = 0x10000000, /** Send queue bypass (when receiving the send queue) */
flag_nat = 0x20000000, /** 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 */
};
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_counts
{
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
{
int sd; /** socket descriptor */
struct event* ev_read; /** libevent struct for read events */
struct event* ev_write; /** libevent struct for write events */
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 */
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 linked_list* feature_cast; /** Features supported by feature cast */
struct adc_message* info; /** ADC 'INF' message (broadcasted to everyone joining the hub) */
size_t send_queue_offset; /** Send queue byte offset */
struct linked_list* send_queue; /** Send queue */
int send_queue_size; /** Size of send queue (in bytes, not messages) */
int send_queue_esize; /** Effective send queue size */
char* recv_buf; /** Recv buffer */
size_t recv_buf_offset; /** Recv buffer offset */
struct hub_info* hub; /** The hub instance this user belong to */
int quit_reason; /** Quit reason (see user_quit_reason) */
struct ip_addr_encap ipaddr; /** IP address of connected user */
struct user_counts limits; /** Data used for limitation */
#ifdef SSL_SUPPORT
SSL* ssl; /** SSL handle */
#endif /* SSL_SUPPORT */
};
/**
* 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);
/**
* Will post a message that will delete the user later.
*/
extern void user_schedule_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.
*/
extern void user_set_info(struct user* user, struct adc_message* info);
/**
* 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, 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);
/**
* 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);
#endif /* HAVE_UHUB_USER_H */

222
src/usermanager.c Normal file
View File

@@ -0,0 +1,222 @@
/*
* 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"
/*
* This callback function is used to clear user objects from the userlist.
* Should only be used in user_manager_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 user_manager_stats(struct hub_info* hub)
{
int factor = 0;
struct net_statistics* total;
struct net_statistics* intermediate;
net_stats_get(&intermediate, &total);
factor = (time(NULL) - intermediate->timestamp);
if (!factor) factor++;
hub_log(log_info, "Statistics users=%zu, net_tx=%d KB/s, net_rx=%d KB/s",
hub->users->count,
(int) ((intermediate->tx / factor) / 1024),
(int) ((intermediate->rx / factor) / 1024));
net_stats_reset();
}
static void timer_statistics(int fd, short ev, void *arg)
{
struct hub_info* hub = (struct hub_info*) arg;
struct timeval timeout = { TIMEOUT_STATS, 0 };
user_manager_stats(hub);
evtimer_set(&hub->ev_timer, timer_statistics, hub);
evtimer_add(&hub->ev_timer, &timeout);
}
int user_manager_init(struct hub_info* hub)
{
struct user_manager* users = NULL;
struct timeval timeout = { TIMEOUT_STATS, 0 };
users = (struct user_manager*) hub_malloc_zero(sizeof(struct user_manager));
users->list = list_create();
users->free_sid = 1;
if (!users->list)
{
list_destroy(users->list);
return -1;
}
hub->users = users;
evtimer_set(&hub->ev_timer, timer_statistics, hub);
evtimer_add(&hub->ev_timer, &timeout);
return 0;
}
void user_manager_shutdown(struct hub_info* hub)
{
struct user_manager* users = hub->users;
event_del(&hub->ev_timer);
list_clear(users->list, &clear_user_list_callback);
list_destroy(users->list);
hub_free(hub->users);
}
void user_manager_add(struct user* user)
{
list_append(user->hub->users->list, user);
user->hub->users->count++;
user->hub->users->count_peak = MAX(user->hub->users->count, user->hub->users->count_peak);
user->hub->users->shared_size += user->limits.shared_size;
user->hub->users->shared_files += user->limits.shared_files;
}
void user_manager_remove(struct user* user)
{
list_remove(user->hub->users->list, user);
user->hub->users->count--;
user->hub->users->shared_size -= user->limits.shared_size;
user->hub->users->shared_files -= user->limits.shared_files;
}
struct user* 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* 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* 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 send_user_list(struct user* target)
{
int ret = 1;
user_flag_set(target, flag_user_list);
struct user* user = (struct user*) list_get_first(target->hub->users->list); /* iterate users - only on INF or PAS msg */
while (user)
{
if (user_is_logged_in(user))
{
ret = route_to_user(target, user->info);
if (!ret)
break;
}
user = (struct user*) list_get_next(user->hub->users->list);
}
if (!target->send_queue_size)
{
user_flag_unset(target, flag_user_list);
}
return ret;
}
void send_quit_message(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(leaving->hub, command);
adc_msg_free(command);
}
sid_t user_manager_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++;
}

101
src/usermanager.h Normal file
View File

@@ -0,0 +1,101 @@
/*
* 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 user_manager_init(struct hub_info* hub);
/**
* Shuts down the user manager.
* All users will be disconnected and deleted as part of this.
*/
extern void user_manager_shutdown(struct hub_info* hub);
/**
* Generate statistics for logfiles.
*/
extern void user_manager_stats(struct hub_info* hub);
/**
* Add a new user to the user manager.
*/
extern void user_manager_add(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.
*/
extern void user_manager_remove(struct user* user);
/**
* Returns a free sid for a new user.
*/
extern sid_t user_manager_get_free_sid(struct hub_info* hub);
/**
* Lookup a user based on the session ID (sid).
* NOTE: This will only search connected users.
* @return a user if found, or NULL if not found
*/
extern struct user* 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* 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* 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 send_user_list(struct user* user);
/**
* Send a quit message to all connected users when 'user' is
* leaving the hub (for whatever reason).
*/
extern void send_quit_message(struct user* user);
#endif /* HAVE_UHUB_USER_MANAGER_H */