From 6ab2bb51466d20b436d2105ff09a28480e97076b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=EFc=20Gomez?= Date: Wed, 26 Sep 2007 14:45:30 +0200 Subject: [PATCH] * src/sample: Update manpage, sample config and sample bip.vim + typo * src/bip: warn the user about rlimits upon start and whenever they're reached * src/bip: add -v flag (show version) * src/bip: add admin option to user block, and restrict some commands to admins. Log whenever a /bip command is used * src/bip: enhance /BIP LIST and HELP commands, add INFO command * src: fix defaults user/nick/realnames not loaded into connections, resulting in segfault (WRITE_LINE1 to NULL) and oidentd.conf file not containing usernames * src: add default.h and version.h * src: fflush() system log every log_sync_interval * src/bip: fix "Resetted." printed whenever a client talks with backlog_reset_ontalk true * src/bip: fix backlog user options not set to defaults in add_user * src/bip: fix oidentd.conf config written lately. Still some issues * src/util: add human readable time hrtime(), bool2text() and ssl checlmode2text() functions * src: lower RECONN_TIMER + add RECONN_TIMER_MAX option * src/bip: code some validate_config --- ChangeLog | 23 ++ bip.conf.1 | 5 + samples/bip.conf | 10 +- samples/bip.vim | 11 +- src/bip.c | 598 +++++++++++++++++++++++++++++++++++++++++------ src/bip.h | 1 + src/conf.y | 3 +- src/defaults.h | 6 + src/irc.c | 35 ++- src/irc.h | 3 + src/lex.l | 1 + src/log.c | 2 + src/util.c | 38 +++ src/util.h | 5 + src/version.h | 1 + 15 files changed, 659 insertions(+), 83 deletions(-) create mode 100644 src/defaults.h create mode 100644 src/version.h diff --git a/ChangeLog b/ChangeLog index d20b921..7c77a4f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2007-09-25 Loïc Gomez + + * src/sample: Update manpage, sample config and sample bip.vim + typo + * src/bip: warn the user about rlimits upon start and whenever they're + reached + * src/bip: add -v flag (show version) + * src/bip: add admin option to user block, and restrict some commands to + admins. Log whenever a /bip command is used + * src/bip: enhance /BIP LIST and HELP commands, add INFO command + * src: fix defaults user/nick/realnames not loaded into connections, + resulting in segfault (WRITE_LINE1 to NULL) and oidentd.conf file not + containing usernames + * src: add default.h and version.h + * src: fflush() system log every log_sync_interval + * src/bip: fix "Resetted." printed whenever a client talks with + backlog_reset_ontalk true + * src/bip: fix backlog user options not set to defaults in add_user + * src/bip: fix oidentd.conf config written lately. Still some issues + * src/util: add human readable time hrtime(), bool2text() and ssl + checlmode2text() functions + * src: lower RECONN_TIMER + add RECONN_TIMER_MAX option + * src/bip: code some validate_config + 2007-05-26 Arnaud Cornet * src: lot's of code cleanup and refactoring. Open door to better diff --git a/bip.conf.1 b/bip.conf.1 index 76ebff5..ee6ac12 100644 --- a/bip.conf.1 +++ b/bip.conf.1 @@ -210,6 +210,11 @@ The user name. It'll be used to authenticate to bip and in \fBlog_format\fP. \fBpassword\fP The password. It \fBMUST\fP be generated with \fBbimkpw\fP or it'll not work. +.TP +\fBadmin\fP (default: \fBfalse\fP) +If a user has admin set to true, he'll become a bip administrator, which allows +him for example to RELOAD bip from IRC or to see the user configuration. + .TP \fBssl_check_mode\fP (default: \fBnone\fP) Tells whether BIP should check the server SSL certificate and against what. diff --git a/samples/bip.conf b/samples/bip.conf index f8ffa33..847de3c 100644 --- a/samples/bip.conf +++ b/samples/bip.conf @@ -25,6 +25,10 @@ log_level = 3; # full log filename. #log_root = "/var/proxy/logs"; +# Uncomment this line to disable bip's internal messages logging. +# This is not recommended, a better option is to reduce log_level. +#log_system = false; + # Log format allows you to make log filenames depend on the log line's # attributes. Here's a list : # %u -> user name @@ -77,6 +81,10 @@ user { # bipmkpw password = "3880f2b39b3b9cb507b052b695d2680859bfc327"; + # Set this to true if you want "bip4ever" to have admin privileges on bip + # He'll be able to RELOAD bip and see all users' configuration (except pass) + admin = true; + # SSL certificates checking mode for user: # - "none" to accept anything; # - "basic" to accept if the certificate is contained in the store; @@ -156,7 +164,7 @@ user { # Password protected channel channel { name = "#elite_UnDeRgR0uNd"; - key = "sikour"; + key = "sikiour"; }; }; diff --git a/samples/bip.vim b/samples/bip.vim index 162bc4d..f4bcadb 100644 --- a/samples/bip.vim +++ b/samples/bip.vim @@ -53,12 +53,10 @@ syn region bipMain start=/\%^/ end=/\%$/ " Top level elements syn keyword bipKeyword contained nextgroup=bipBoolV client_side_ssl - \ no_backlog always_backlog bl_msg_only blreset_on_talk - \ backlog_no_timestamp backlog syn keyword bipKeyword contained nextgroup=bipStringV log_root \ log_format pid_file syn keyword bipKeyword contained nextgroup=bipNumericV port log_level - \ backlog_lines log_sync_interval + \ log_sync_interval syn keyword bipKeyword contained nextgroup=bipIPV ip " Network block (level 1) @@ -71,10 +69,15 @@ syn keyword bipNKeyword contained nextgroup=bipBoolV ssl " User block (level 1) syn region bipUser contained matchgroup=Macro start=/user\s*{\s*/ \ end=/};/ - \ contains=bipUKeyword,bipConnection,bipComment,bipEndError,bipWhite + \ contains=bipUKeyword,bipUBool,bipConnection,bipComment,bipEndError,bipWhite syn keyword bipUKeyword contained nextgroup=bipStringV password name \ default_nick default_user default_realname ssl_check_store \ ssl_check_mode +syn keyword bipUKeyword contained nextgroup=bipNumericV backlog_lines +syn keyword bipUBool contained nextgroup=bipBoolV admin + \ no_backlog always_backlog bl_msg_only blreset_on_talk + \ backlog_no_timestamp backlog log_system backlog_reset_on_talk + \ backlog_msg_only backlog_always " Connection block (level 2) syn region bipConnection contained matchgroup=Macro diff --git a/src/bip.c b/src/bip.c index 74d3dbc..035488b 100644 --- a/src/bip.c +++ b/src/bip.c @@ -17,13 +17,16 @@ #include #include #include +#include +#include #include "irc.h" #include "conf.h" #include "tuple.h" #include "log.h" -#include "irc.h" #include "bip.h" #include "line.h" +#include "version.h" +#include "defaults.h" int sighup = 0; @@ -41,8 +44,6 @@ char *conf_pid_file; char *conf_biphome; /* log options, for sure the trickiest :) */ -/* no backlog at all */ -int conf_backlog = 0; extern int conf_memlog; int conf_log = 0; int conf_log_system = 0; @@ -54,6 +55,8 @@ static void conf_die(char *fmt, ...); int adm_trust(struct link_client *ic, struct line *line); #endif static char *get_tuple_value(list_t *tuple_l, int lex); +void adm_reply(struct link_client *ic, char *str); +void adm_list_connections(struct link_client *ic, struct user *bu); static void hash_binary(char *hex, unsigned char **password, unsigned int *seed) { @@ -246,10 +249,19 @@ static void usage(char *name) " -f config_file: Use config_file as the configuration file\n" " If no config file is given %s will try to open ~" S_CONF "\n" " -n: Don't daemonize, log in stderr\n" +" -v: Print version and exit\n" " -h: This help\n", name, name); exit(1); } +static void version() +{ + printf( +"Bip IRC Proxy - %s\n" +"Copyright © Arnaud Cornet and Loïc Gomez (2004 - 2007)\n" +"Distributed under the GNU Public License Version 2\n", BIP_VERSION); +} + void reload_config(int i) { (void)i; @@ -258,6 +270,21 @@ void reload_config(int i) bip_t *_bip; +void rlimit_cpu_reached(int i) +{ + mylog(LOG_WARN, "This process has reached the CPU time usage limit. " + "It means bip'll be killed by the Operating System in a short " + "notice. We advise you to use a crontab to restart bip " + "whenever this happens."); +} + +void rlimit_bigfile_reached(int i) +{ + mylog(LOG_WARN, "A file has reached the max size this process is " + "allowed to create. The file will not be written correctly, " + "an error message should follow. This is not fatal."); +} + void bad_quit(int i) { list_iterator_t it; @@ -419,6 +446,12 @@ static int add_connection(bip_t *bip, struct user *user, list_t *data) /* checks that can only be here, or must */ if (!l->network) conf_die("Missing network in connection block"); + if (!l->connect_nick) + l->connect_nick = strdup(user->default_nick); + if (!l->username) + l->username = strdup(user->default_username); + if (!l->realname) + l->realname = strdup(user->default_realname); return 1; } @@ -452,6 +485,13 @@ static int add_user(bip_t *bip, list_t *data) u = calloc(sizeof(struct user), 1); hash_insert(&bip->users, name, u); hash_init(&u->connections, HASH_NOCASE); + u->admin = 0; + u->backlog = DEFAULT_LEX_BACKLOG; + u->always_backlog = DEFAULT_LEX_ALWAYS_BACKLOG; + u->bl_msg_only = DEFAULT_LEX_BL_MSG_ONLY; + u->backlog_lines = DEFAULT_LEX_BACKLOG_LINES; + u->backlog_no_timestamp = DEFAULT_LEX_BACKLOG_NO_TIMESTAMP; + u->blreset_on_talk = DEFAULT_LEX_BLRESET_ON_TALK; } else { FREE(u->name); FREE(u->password); @@ -468,6 +508,9 @@ static int add_user(bip_t *bip, list_t *data) case LEX_NAME: MOVE_STRING(u->name, t->pdata); break; + case LEX_ADMIN: + u->admin = t->ndata; + break; case LEX_PASSWORD: hash_binary(t->pdata, &u->password, &u->seed); free(t->pdata); @@ -560,9 +603,17 @@ static int validate_config(bip_t *bip) r = 0; } } + + if (user->backlog && !conf_log && user->backlog_lines == 0) { + conf_die("If conf_log = false, you must set backlog_" + "lines to a non-nul value for each user with" + "backlog = true. Faulty user is %s", + user->name); + } } #warning CODE ME +#warning DONE BY KYOSHIRO :p #if 0 if (conf_backlog && !conf_log) { if (conf_backlog_lines == 0) { @@ -591,26 +642,6 @@ int fireup(bip_t *bip, FILE *conf) case LEX_LOG_SYNC_INTERVAL: conf_log_sync_interval = t->ndata; break; -/* - case LEX_ALWAYS_BACKLOG: - conf_always_backlog = t->ndata; - break; - case LEX_BACKLOG: - conf_backlog = t->ndata; - break; - case LEX_BL_MSG_ONLY: - conf_bl_msg_only = t->ndata; - break; - case LEX_BACKLOG_LINES: - conf_backlog_lines = t->ndata; - break; - case LEX_BACKLOG_NO_TIMESTAMP: - conf_backlog_no_timestamp = t->ndata; - break; - case LEX_BLRESET_ON_TALK: - conf_blreset_on_talk = t->ndata; - break; -*/ case LEX_LOG: conf_log = t->ndata; break; @@ -677,6 +708,73 @@ static void log_file_setup(void) } } +void check_rlimits() { + int r, cklim; + struct rlimit lt; + + cklim = 0; + + r = getrlimit(RLIMIT_AS, <); + if (r) { + mylog(LOG_ERROR, "getrlimit(): failed with %s", strerror(errno)); + } else { + if (lt.rlim_max != RLIM_INFINITY) { + mylog(LOG_WARN, "virtual memory rlimit active" + ", bip may be KILLED by the system"); + cklim = 1; + } + } + + r = getrlimit(RLIMIT_CPU, <); + if (r) { + mylog(LOG_ERROR, "getrlimit(): failed with %s", strerror(errno)); + } else { + if (lt.rlim_max != RLIM_INFINITY) { + mylog(LOG_WARN, "CPU rlimit active, bip may " + "be OFTEN KILLED by the system"); + cklim = 1; + } + } + + r = getrlimit(RLIMIT_FSIZE, <); + if (r) { + mylog(LOG_ERROR, "getrlimit(): failed with %s", strerror(errno)); + } else { + if (lt.rlim_max != RLIM_INFINITY) { + mylog(LOG_WARN, "FSIZE rlimit active, bip'll" + " fail to create files of size greater than " + "%d bytes.", (int)lt.rlim_max); + cklim = 1; + } + } + + r = getrlimit(RLIMIT_NOFILE, <); + if (r) { + mylog(LOG_ERROR, "getrlimit(): failed with %s", strerror(errno)); + } else { + if (lt.rlim_max != RLIM_INFINITY) { + mylog(LOG_WARN, "opened files count rlimit " + "active, bip'll not be allowed to open more" + " than %d files at a time", (int)lt.rlim_max); + cklim = 1; + } + } + + r = getrlimit(RLIMIT_STACK, <); + if (r) { + mylog(LOG_ERROR, "getrlimit(): failed with %s", strerror(errno)); + } else { + if (lt.rlim_max != RLIM_INFINITY) { + mylog(LOG_WARN, "stack rlimit active" + ", bip may be KILLED by the system"); + cklim = 1; + } + } + + if (cklim) + mylog(LOG_WARN, "You can check your limits with `ulimit -a'"); +} + int main(int argc, char **argv) { FILE *conf = NULL; @@ -698,11 +796,13 @@ int main(int argc, char **argv) signal(SIGINT, bad_quit); signal(SIGQUIT, bad_quit); signal(SIGTERM, bad_quit); + signal(SIGXFSZ, rlimit_bigfile_reached); + /* TODO handle SIGXCPU => soft CPU limit reached */ + signal(SIGXCPU, rlimit_cpu_reached); conf_log_root = NULL; conf_log_format = NULL; conf_log_level = LOG_INFO; - conf_backlog = 1; conf_log = 1; conf_log_system = 1; conf_log_sync_interval = 5; @@ -710,7 +810,7 @@ int main(int argc, char **argv) conf_global_log_file = stderr; conf_pid_file = NULL; - while ((ch = getopt(argc, argv, "hnf:s:")) != -1) { + while ((ch = getopt(argc, argv, "hvnf:s:")) != -1) { switch (ch) { case 'f': confpath = strdup(optarg); @@ -721,13 +821,20 @@ int main(int argc, char **argv) case 's': conf_biphome = strdup(optarg); break; + case 'v': + version(); + exit(0); + break; default: + version(); usage(argv[0]); } } umask(0027); + check_rlimits(); + if (confpath) { conf = fopen(confpath, "r"); if (!conf) @@ -837,30 +944,302 @@ int main(int argc, char **argv) return 1; } -void write_user_list(connection_t *c, char *dest) +void adm_print_connection(struct link_client *ic, struct link *lnk, struct user *bu) +{ + hash_iterator_t lit; + char buf[4096]; + int t_wrote = 0; + int t_max_len = 70; + + if (!bu) { + bu = lnk->user; + snprintf(buf, 4095, "%s's links:", bu->name); + buf[4095] = 0; + adm_reply(ic, buf); + } + + snprintf(buf, 4095, "* %s to %s as \"%s\" (%s!%s) :", + lnk->name, lnk->network->name, + (lnk->realname ? lnk->realname : bu->default_realname), + (lnk->connect_nick ? lnk->connect_nick : bu->default_nick), + (lnk->username ? lnk->username : bu->default_username) + ); + buf[4095] = 0; + adm_reply(ic, buf); + + t_wrote += snprintf(buf, 4095, " Options:"); + if (lnk->follow_nick) + t_wrote += snprintf(buf + t_wrote, + 4095 - t_wrote, " follow_nick"); + if (lnk->ignore_first_nick) + t_wrote += snprintf(buf + t_wrote, + 4095 - t_wrote, " ignore_first_nick"); + if (lnk->away_nick) + t_wrote += snprintf(buf + t_wrote, + 4095 - t_wrote, " away_nick=%s", + lnk->away_nick); + if (lnk->no_client_away_msg) + t_wrote += snprintf(buf + t_wrote, + 4095 - t_wrote, " no_client_away_msg=%s", + lnk->no_client_away_msg); + if (lnk->vhost) + t_wrote += snprintf(buf + t_wrote, + 4095 - t_wrote, " vhost=%s", + lnk->vhost); + if (lnk->bind_port) + t_wrote += snprintf(buf + t_wrote, + 4095 - t_wrote, " bind_port=%u", + lnk->bind_port); + buf[4095] = 0; + adm_reply(ic, buf); + + // TODO: on_connect_send + + // TODO : check channels struct + t_wrote = snprintf(buf, 4095, " Channels:"); + for (hash_it_init(&lnk->chan_infos, &lit); hash_it_item(&lit); + hash_it_next(&lit)) { + struct channel *ch = hash_it_item(&lit); + + if (ch->key) { + t_wrote += snprintf(buf + t_wrote, 4095 + - t_wrote, " *%s", ch->name); + } else { + t_wrote += snprintf(buf + t_wrote, 4095 + - t_wrote, " %s", ch->name); + } + if (t_wrote > t_max_len) { + buf[4095] = 0; + adm_reply(ic, buf); + t_wrote = 0; + } + } + buf[4095] = 0; + adm_reply(ic, buf); + + t_wrote = snprintf(buf, 4095, " Status: "); + switch (lnk->s_state) { + case IRCS_NONE: + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, + "not started"); + break; + case IRCS_CONNECTING: + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, + "connecting... attempts: %d, last: %s", + lnk->s_conn_attempt, + hrtime(lnk->last_connection_attempt)); + break; + case IRCS_CONNECTED: + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, + "connected !"); + break; + case IRCS_WAS_CONNECTED: + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, + "disconnected, attempts: %d, last: %s", + lnk->s_conn_attempt, + hrtime(lnk->last_connection_attempt)); + break; + case IRCS_RECONNECTING: + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, + "reconnecting... attempts: %d, last: %s", + lnk->s_conn_attempt, + hrtime(lnk->last_connection_attempt)); + break; + case IRCS_TIMER_WAIT: + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, + "waiting to reconnect, attempts: %d, last: %s", + lnk->s_conn_attempt, + hrtime(lnk->last_connection_attempt)); + break; + default: + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, + "unknown"); + break; + // s_conn_attempt recon_timer last_connection_attempt + } + buf[4095] = 0; + adm_reply(ic, buf); +} + +void adm_list_all_links(struct link_client *ic) +{ + list_iterator_t it; + for (list_it_init(&_bip->link_list, &it); list_it_item(&it); + list_it_next(&it)) { + struct link *l = list_it_item(&it); + if (l) + adm_print_connection(ic, l, NULL); + } +} + +void adm_list_all_connections(struct link_client *ic) +{ + hash_iterator_t it; + for (hash_it_init(&_bip->users, &it); hash_it_item(&it); + hash_it_next(&it)) { + struct user *u = hash_it_item(&it); + if (u) + adm_list_connections(ic, u); + } +} + +void adm_info_user(struct link_client *ic, char *name) +{ + struct user *u; + char buf[4096]; + int t_wrote = 0; + + u = hash_get(&_bip->users, name); + if (!u) { + adm_reply(ic, "Unknown user"); + return; + } + + //t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, ""); + //buf[4095] = 0; + //adm_reply(ic, buf); + //t_wrote = 0; + + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, "user: %s", u->name); + if (u->admin) + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, ", is bip admin"); + + buf[4095] = 0; + adm_reply(ic, buf); + t_wrote = 0; + +#ifdef HAVE_LIBSSL + snprintf(buf, 4095, "SSL check mode '%s', stored into '%s'", + checkmode2text(u->ssl_check_mode), u->ssl_check_store); + buf[4095] = 0; + adm_reply(ic, buf); +#endif + snprintf(buf, 4095, "Defaults nick: %s, user: %s, realname: %s", + u->default_nick, u->default_username, u->default_realname); + buf[4095] = 0; + adm_reply(ic, buf); + if (u->backlog) { + snprintf(buf, 4095, "Backlog enabled, lines: %d, no timestamp: " + "%s, messages only: %s", u->backlog_lines, + bool2text(u->backlog_no_timestamp), + bool2text(u->bl_msg_only)); + buf[4095] = 0; + adm_reply(ic, buf); + snprintf(buf, 4095, "always backlog: %s, reset on talk: %s", + bool2text(u->always_backlog), + bool2text(u->blreset_on_talk)); + buf[4095] = 0; + adm_reply(ic, buf); + } else { + adm_reply(ic, "Backlog disabled"); + } + adm_list_connections(ic, u); +} + +void adm_list_users(struct link_client *ic) { hash_iterator_t it; hash_iterator_t lit; char buf[4096]; + connection_t *c; - WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, "bip user list:"); + c = CONN(ic); + + adm_reply(ic, "bip user list:"); for (hash_it_init(&_bip->users, &it); hash_it_item(&it); hash_it_next(&it)) { struct user *u = hash_it_item(&it); + int t_max_len = 60; + int first = 1; + int t_wrote = 0; - snprintf(buf, 4095, "* %s:", u->name); buf[4095] = 0; - WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, buf); + t_wrote += snprintf(buf, 4095, "* %s%s:", u->name, (u->admin ? + "": "(admin)")); for (hash_it_init(&u->connections, &lit); hash_it_item(&lit); hash_it_next(&lit)) { - struct link *con = hash_it_item(&lit); - snprintf(buf, 4095, " - %s", con->name); - buf[4095] = 0; - WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, buf); + struct link *lnk = hash_it_item(&lit); + if (first) + first = 0; + else + t_wrote += snprintf(buf + t_wrote, 4095 + - t_wrote, ","); + + t_wrote += snprintf(buf + t_wrote, 4095 - t_wrote, + " %s", lnk->name); + if (t_wrote > t_max_len) { + buf[4095] = 0; + adm_reply(ic, buf); + t_wrote = 0; + } } + buf[4095] = 0; + adm_reply(ic, buf); } - WRITE_LINE2(c, P_IRCMASK, "PRIVMSG", dest, - "End of bip user list"); + adm_reply(ic, "End of bip user list"); +} + +void adm_list_networks(struct link_client *ic) +{ + hash_iterator_t it; + char buf[4096]; + connection_t *c; + + c = CONN(ic); + + adm_reply(ic, "bip network list (* means SSL):"); + for (hash_it_init(&_bip->networks, &it); hash_it_item(&it); + hash_it_next(&it)) { + struct network *n = hash_it_item(&it); + int t_max_len = 60; + int t_wrote = 0; + int i; + + buf[4095] = 0; + if (n->ssl) { + t_wrote += snprintf(buf, 4095, "- %s*:", n->name); + } else { + t_wrote += snprintf(buf, 4095, "- %s:", n->name); + } + for (i = 0; i < n->serverc; i++) { + struct server *serv = i+n->serverv; + t_wrote += snprintf(buf + t_wrote, 4095 + - t_wrote, " %s:%d", serv->host, + serv->port); + if (t_wrote > t_max_len) { + buf[4095] = 0; + adm_reply(ic, buf); + t_wrote = 0; + } + } + buf[4095] = 0; + adm_reply(ic, buf); + } + adm_reply(ic, "End of bip network list"); +} + +void adm_list_connections(struct link_client *ic, struct user *bu) +{ + hash_iterator_t it; + char buf[4096]; + connection_t *c; + + c = CONN(ic); + if (!bu) { + adm_reply(ic, "Your connections:"); + bu = LINK(ic)->user; + } else { + snprintf(buf, 4095, "%s's connections:", bu->name); + buf[4095] = 0; + adm_reply(ic, buf); + } + + for (hash_it_init(&bu->connections, &it); hash_it_item(&it); + hash_it_next(&it)) { + struct link *lnk= hash_it_item(&it); + adm_print_connection(ic, lnk, bu); + } + adm_reply(ic, "End of bip connection list"); } #ifdef HAVE_LIBSSL @@ -1022,7 +1401,7 @@ extern struct link_client *reloading_client; void adm_blreset(struct link_client *ic) { log_reinit_all(LINK(ic)->log); - adm_reply(ic, "Resetted."); + adm_reply(ic, "backlog resetted for this network."); } void adm_follow_nick(struct link_client *ic, char *val) @@ -1061,7 +1440,7 @@ void adm_on_connect_send(struct link_client *ic, char *val) s = list_remove_last(&link->on_connect_send); if (s) free(s); - adm_reply(ic, "on_connect_send emtpy."); + adm_reply(ic, "on_connect_send cleared."); } } @@ -1074,78 +1453,161 @@ void adm_away_nick(struct link_client *ic, char *val) } if (val != NULL) { link->away_nick = strdup(val); - adm_reply(ic, "away_nick emtpy."); - } else { adm_reply(ic, "away_nick set."); + } else { + adm_reply(ic, "away_nick cleared."); } } +void adm_bip_help(struct link_client *ic, int admin) +{ + if (admin) { + adm_reply(ic, "/BIP RELOAD # Re-read bip configuration " + "and apply changes. /!\\ VERY UNSTABLE !"); + adm_reply(ic, "/BIP INFO user # show a user's " + "configuration"); + adm_reply(ic, "/BIP LIST networks|users|connections|all_links" + "|all_connections"); + } else { + adm_reply(ic, "/BIP LIST networks|connections"); + } + adm_reply(ic, "/BIP JUMP # jump to next server (in same network)"); + adm_reply(ic, "/BIP BLRESET # reset backlog (this connection only)"); +#ifdef HAVE_LIBSSL + adm_reply(ic, "/BIP TRUST # trust this server certificate"); +#endif + adm_reply(ic, "/BIP HELP # show this help..."); + adm_reply(ic, "## Temporary changes for this connection:"); + adm_reply(ic, "/BIP FOLLOW_NICK|IGNORE_FIRST_NICK TRUE|FALSE"); + adm_reply(ic, "/BIP ON_CONNECT_SEND # Adds a string to " + "send on connect"); + adm_reply(ic, "/BIP ON_CONNECT_SEND # Clears on_connect_send"); + adm_reply(ic, "/BIP AWAY_NICK # Set away nick"); + adm_reply(ic, "/BIP AWAY_NICK # clear away nick"); +} + int adm_bip(struct link_client *ic, struct line *line, unsigned int privmsg) { - char *nick; + int admin = LINK(ic)->user->admin; - if (LINK(ic)->l_server) - nick = LINK(ic)->l_server->nick; - else - nick = LINK(ic)->prev_nick; if (line->elemc < privmsg + 2) return OK_FORGET; + mylog(LOG_STD, "/BIP %s from %s", line->elemv[privmsg + 1], + LINK(ic)->user->name); if (strcasecmp(line->elemv[privmsg + 1], "RELOAD") == 0) { + if (!admin) { + adm_reply(ic, "You're not allowed to reload bip"); + return OK_FORGET; + } + adm_reply(ic, "Bip has been set to reload shortly"); reloading_client = ic; sighup = 1; } else if (strcasecmp(line->elemv[privmsg + 1], "LIST") == 0) { - write_user_list(CONN(ic), nick); + if (line->elemc != privmsg + 3) { + adm_reply(ic, "LIST command needs one argument"); + return OK_FORGET; + } + + if (admin && strncasecmp(line->elemv[privmsg + 2], + "users", 5) == 0) { + adm_list_users(ic); + } else if (strncasecmp(line->elemv[privmsg + 2], + "networks", 8) == 0) { + adm_list_networks(ic); + } else if (strncasecmp(line->elemv[privmsg + 2], + "connections", 11) == 0) { + adm_list_connections(ic, NULL); + } else if (admin && strncasecmp(line->elemv[privmsg + 2], + "all_connections", 15) == 0) { + adm_list_all_connections(ic); + } else if (admin && strncasecmp(line->elemv[privmsg + 2], + "all_links", 9) == 0) { + adm_list_all_links(ic); + } else { + adm_reply(ic, "Invalid LIST request"); + } + } else if (strcasecmp(line->elemv[privmsg + 1], "INFO") == 0) { + if (line->elemc < privmsg + 3) { + adm_reply(ic, "INFO command needs at least one argument"); + return OK_FORGET; + } + + if (admin && strncasecmp(line->elemv[privmsg + 2], + "user", 5) == 0) { + if (line->elemc == privmsg + 4) { + adm_info_user(ic, line->elemv[privmsg + 3]); + } else { + adm_reply(ic, "/BIP INFO user needs one " + "argument"); + } + /*TODO } else if (strncasecmp(line->elemv[privmsg + 2], + "network", 8) == 0) { + if (line->elemc == privmsg + 4) { + adm_info_network(ic, line->elemv[privmsg + 3]); + } else { + adm_reply(ic, "/BIP INFO network needs one " + "argument"); + }*/ + } else { + adm_reply(ic, "Invalid INFO request"); + } } else if (strcasecmp(line->elemv[privmsg + 1], "JUMP") == 0) { if (LINK(ic)->l_server) { WRITE_LINE1(CONN(LINK(ic)->l_server), NULL, "QUIT", "jumpin' jumpin'"); connection_close(CONN(LINK(ic)->l_server)); } + adm_reply(ic, "Jumping to next server"); } else if (strcasecmp(line->elemv[privmsg + 1], "BLRESET") == 0) { adm_blreset(ic); } else if (strcasecmp(line->elemv[privmsg + 1], "HELP") == 0) { - WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, - "/BIP (RELOAD|LIST|JUMP|BLRESET|HELP|TRUST)"); - WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, - "/BIP FOLLOW_NICK|IGNORE_FIRST_NICK TRUE|FALSE"); - WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, - "/BIP ON_CONNECT_SEND # Adds a string to " - "send on connect"); - WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, - "/BIP ON_CONNECT_SEND # Clears on_connect_send"); - WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, - "/BIP AWAY_NICK # Set away nick"); - WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick, - "/BIP AWAY_NICK # clear away nick"); + adm_bip_help(ic, admin); } else if (strcasecmp(line->elemv[privmsg + 1], "FOLLOW_NICK") == 0) { - if (line->elemc != privmsg + 3) + if (line->elemc != privmsg + 3) { + adm_reply(ic, "FOLLOW_NICK command needs one argument"); return OK_FORGET; + } adm_follow_nick(ic, line->elemv[privmsg + 2]); } else if (strcasecmp(line->elemv[privmsg + 1], "IGNORE_FIRST_NICK") == 0) { - if (line->elemc != privmsg + 3) + if (line->elemc != privmsg + 3) { + adm_reply(ic, "IGNORE_FIRST_NICK command needs one argument"); return OK_FORGET; + } adm_ignore_first_nick(ic, line->elemv[privmsg + 2]); } else if (strcasecmp(line->elemv[privmsg + 1], "ON_CONNECT_SEND") == 0) { - if (line->elemc == privmsg + 2) + if (line->elemc == privmsg + 2) { adm_on_connect_send(ic, NULL); - else if (line->elemc == privmsg + 3) + } else if (line->elemc == privmsg + 3) { + // TODO: on connect send should not be limited to one word adm_on_connect_send(ic, line->elemv[privmsg + 2]); - else - return OK_FORGET; + } else { + adm_reply(ic, "/BIP ON_CONNECT_SEND needs zero or one " + "argument"); + } } else if (strcasecmp(line->elemv[privmsg + 1], "AWAY_NICK") == 0) { - if (line->elemc == privmsg + 2) + if (line->elemc == privmsg + 2) { adm_away_nick(ic, NULL); - else if (line->elemc == privmsg + 3) + } else if (line->elemc == privmsg + 3) { adm_away_nick(ic, line->elemv[privmsg + 2]); - else - return OK_FORGET; + } else { + adm_reply(ic, "/BIP AWAY_NICK needs zero or one " + "argument"); + } #ifdef HAVE_LIBSSL } else if (strcasecmp(line->elemv[privmsg + 1], "TRUST") == 0) { + /* TODO : warn the user of results */ return adm_trust(ic, line); #endif + } else { + char buf[4096]; + + buf[4095] = 0; + snprintf(buf, 4095, "Unknown command %s", + line->elemv[privmsg + 1]); + adm_reply(ic, buf); } return OK_FORGET; } diff --git a/src/bip.h b/src/bip.h index 61942ea..0c34e83 100644 --- a/src/bip.h +++ b/src/bip.h @@ -32,6 +32,7 @@ struct c_connection char *no_client_away_msg; struct client *client; + struct c_user *bipuser; }; struct c_channel diff --git a/src/conf.y b/src/conf.y index 7edb171..c58f6a5 100644 --- a/src/conf.y +++ b/src/conf.y @@ -80,7 +80,7 @@ struct tuple *tuple_l_new(int type, void *p) %} -%token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY +%token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY LEX_ADMIN %union { int number; @@ -137,6 +137,7 @@ usr_command: $$ = tuple_s_new(LEX_NAME, $3); } | LEX_PASSWORD LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_PASSWORD, $3); } + | LEX_ADMIN LEX_EQ LEX_BOOL { $$ = tuple_i_new(LEX_ADMIN, $3); } | LEX_SSL_CHECK_MODE LEX_EQ LEX_STRING { $$ = tuple_s_new( LEX_SSL_CHECK_MODE, $3); } | LEX_SSL_CHECK_STORE LEX_EQ LEX_STRING { $$ = tuple_s_new( diff --git a/src/defaults.h b/src/defaults.h new file mode 100644 index 0000000..e9d6842 --- /dev/null +++ b/src/defaults.h @@ -0,0 +1,6 @@ +#define DEFAULT_LEX_BACKLOG 1 +#define DEFAULT_LEX_ALWAYS_BACKLOG 0 +#define DEFAULT_LEX_BL_MSG_ONLY 0 +#define DEFAULT_LEX_BACKLOG_LINES 10 +#define DEFAULT_LEX_BACKLOG_NO_TIMESTAMP 0 +#define DEFAULT_LEX_BLRESET_ON_TALK 0 diff --git a/src/irc.c b/src/irc.c index d47f64d..c04ed1a 100644 --- a/src/irc.c +++ b/src/irc.c @@ -25,6 +25,7 @@ #define S_CONN_DELAY (10) extern int sighup; +extern bip_t *_bip; static int irc_join(struct link_server *server, struct line *line); static int irc_part(struct link_server *server, struct line *line); @@ -44,13 +45,12 @@ static int irc_367(struct link_server *server, struct line *l); static int irc_368(struct link_server *server, struct line *l); void irc_server_shutdown(struct link_server *s); static int origin_is_me(struct line *l, struct link_server *server); +void oidentd_dump(list_t *connl); void irc_client_free(struct link_client *cli); -extern int conf_backlog; extern int conf_log_sync_interval; extern int conf_error; extern char conf_errstr[]; -extern int conf_blreset_on_talk; void write_user_list(connection_t *c, char *dest); @@ -61,6 +61,7 @@ static void irc_cli_make_join(struct link_client *ic); #define LAGOUT_TIME 480 #define LAGCHECK_TIME (90) #define RECONN_TIMER (120) +#define RECONN_TIMER_MAX (600) #define LOGGING_TIMEOUT (360) #define CONN_INTERVAL 60 #define CONNECT_TIMEOUT 60 @@ -488,6 +489,11 @@ int irc_dispatch_server(bip_t *bip, struct link_server *server, static void irc_send_join(struct link_client *ic, struct channel *chan) { char *ircmask; /* fake an irc mask for rbot */ + struct user *user; + + user = LINK(ic)->user; + if (!user) + fatal("irc_send_join: No user associated"); ircmask = malloc(strlen(LINK(ic)->l_server->nick) + strlen("!bip@bip.bip.bip") + 1); @@ -503,7 +509,7 @@ static void irc_send_join(struct link_client *ic, struct channel *chan) chan->name, chan->creator, chan->create_ts); /* XXX: could be more efficient */ - if (conf_backlog && log_has_backlog(LINK(ic)->log, chan->name)) { + if (user->backlog && log_has_backlog(LINK(ic)->log, chan->name)) { char *line; int skip = 0; while ((line = @@ -850,7 +856,7 @@ static int irc_cli_privmsg(struct link_client *ic, struct line *line) return adm_bip(ic, line, 1); if (LINK(ic)->user->blreset_on_talk) - adm_blreset(ic); + log_reinit_all(LINK(ic)->log); return OK_COPY_CLI; } @@ -1927,8 +1933,11 @@ static void irc_close(struct link_any *l) server_cleanup(is); if (LINK(is)->last_connection_attempt && time(NULL) - LINK(is)->last_connection_attempt - < CONN_INTERVAL) + < CONN_INTERVAL) { timer = RECONN_TIMER * (LINK(is)->s_conn_attempt); + if (timer > RECONN_TIMER_MAX) + timer = RECONN_TIMER_MAX; + } mylog(LOG_ERROR, "%s dead, reconnecting in %d seconds", LINK(l)->name, timer); LINK(is)->recon_timer = timer; @@ -2001,8 +2010,12 @@ connection_t *irc_server_connect(struct link *link) fatal("connection_new"); ls = irc_server_new(link, conn); - conn->user_data = ls; + + list_add_last(&_bip->conn_list, conn); +#ifdef HAVE_OIDENTD + oidentd_dump(&_bip->conn_list); +#endif irc_server_startup(ls); return conn; } @@ -2139,8 +2152,12 @@ void oidentd_dump(list_t *connl) for (list_it_init(connl, &it); list_it_item(&it); list_it_next(&it)) { connection_t *c = list_it_item(&it); struct link_any *la = c->user_data; - if (c->connected == CONN_OK && la && - TYPE(la) == IRC_TYPE_SERVER) { + if (la && TYPE(la) == IRC_TYPE_SERVER && ( + c->connected == CONN_OK || + c->connected == CONN_NEED_SSLIZE || + c->connected == CONN_INPROGRESS || + c->connected == CONN_NEW || + c->connected == CONN_UNTRUSTED)) { struct link_server *ls; struct link *l; char *localip, *remoteip; @@ -2238,7 +2255,6 @@ void bip_tick(bip_t *bip) if (link->recon_timer == 0) { connection_t *conn; conn = irc_server_connect(link); - list_add_last(&bip->conn_list, conn); link->last_connection_attempt = time(NULL); } else { link->recon_timer--; @@ -2410,6 +2426,7 @@ struct link *irc_link_new() if (!link) fatal("calloc"); + link->l_server = NULL; hash_init(&link->chan_infos, HASH_NOCASE); list_init(&link->chan_infos_order, list_ptr_cmp); list_init(&link->on_connect_send, list_ptr_cmp); diff --git a/src/irc.h b/src/irc.h index f510e38..d8d084e 100644 --- a/src/irc.h +++ b/src/irc.h @@ -68,6 +68,7 @@ struct user { char *name; unsigned char *password; unsigned int seed; + int admin; /* common link options */ @@ -83,6 +84,8 @@ struct user { int backlog_no_timestamp; int blreset_on_talk; + int ssl_check_mode; + char *ssl_check_store; hash_t connections; }; diff --git a/src/lex.l b/src/lex.l index 2819cb0..e1fab54 100644 --- a/src/lex.l +++ b/src/lex.l @@ -63,6 +63,7 @@ list_t *parse_conf(FILE *file) "host" { return LEX_HOST; } "name" { return LEX_NAME; } "user" { return LEX_USER; } +"admin" { return LEX_ADMIN; } "connection" { return LEX_CONNECTION; } "nick" { return LEX_NICK; } "realname" { return LEX_REALNAME; } diff --git a/src/log.c b/src/log.c index 8de219c..3105059 100644 --- a/src/log.c +++ b/src/log.c @@ -23,6 +23,7 @@ extern char *conf_log_format; extern int conf_log; int conf_memlog = 1; +extern FILE *conf_global_log_file; int log_set_backlog_offset(log_t *logdata, char *dest); static int _log_write(log_t *logdata, logfilegroup_t *lf, char *d, char *str); @@ -1113,6 +1114,7 @@ void log_flush_all(void) if (!log_all_logs) return; + fflush(conf_global_log_file); for (list_it_init(log_all_logs, &li); list_it_item(&li); list_it_next(&li)) { log_t *log = list_it_item(&li); diff --git a/src/util.c b/src/util.c index 59453b6..fecb1b0 100644 --- a/src/util.c +++ b/src/util.c @@ -12,6 +12,7 @@ */ #include "config.h" +#include "connection.h" #include "util.h" #include #include @@ -74,6 +75,43 @@ char *timestamp(void) return ts; } +char *hrtime(time_t s) +{ + static char ts[20]; + struct tm *tm; + + if (s == 0) + return "never"; + tm = localtime(&s); + + snprintf(ts, 20, "%02d-%02d-%04d %02d:%02d:%02d", tm->tm_mday, + tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, + tm->tm_min, tm->tm_sec); + return ts; +} + +#ifdef HAVE_LIBSSL +char *checkmode2text(int v) +{ + switch (v) { + case SSL_CHECK_BASIC: + return "basic"; + case SSL_CHECK_CA: + return "ca"; + default: + return "none"; + } +} +#endif + +char *bool2text(int v) +{ + if (v) + return "true"; + else + return "false"; +} + extern FILE *conf_global_log_file; void _mylog(int level, char *fmt, va_list ap) diff --git a/src/util.h b/src/util.h index 1126541..e939fbc 100644 --- a/src/util.h +++ b/src/util.h @@ -113,5 +113,10 @@ char *strmaydup(char *s); void strucase(char *s); int ischannel(char p); +char *hrtime(time_t t); +#ifdef HAVE_LIBSSL +char *checkmode2text(int v); +#endif +char *bool2text(int v); #endif diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..f8fe497 --- /dev/null +++ b/src/version.h @@ -0,0 +1 @@ +#define BIP_VERSION "0.6.1"