diff options
Diffstat (limited to 'src/irc/bot/botnet.c')
-rw-r--r-- | src/irc/bot/botnet.c | 622 |
1 files changed, 622 insertions, 0 deletions
diff --git a/src/irc/bot/botnet.c b/src/irc/bot/botnet.c new file mode 100644 index 00000000..9331e1d4 --- /dev/null +++ b/src/irc/bot/botnet.c @@ -0,0 +1,622 @@ +/* + botnet.c : IRC bot plugin for irssi + + Copyright (C) 1999-2000 Timo Sirainen + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "network.h" +#include "net-nonblock.h" +#include "signals.h" +#include "commands.h" +#include "misc.h" +#include "line-split.h" +#include "lib-config/iconfig.h" + +#include "botnet.h" + +void botnet_connection_init(void); +void botnet_connection_deinit(void); + +static GSList *botnets; + +void bot_send_cmd(BOT_REC *bot, char *data) +{ + g_return_if_fail(bot != NULL); + g_return_if_fail(data != NULL); + + net_transmit(bot->handle, data, strlen(data)); + net_transmit(bot->handle, "\n", 1); +} + +void bot_send_cmdv(BOT_REC *bot, char *format, ...) +{ + va_list args; + char *str; + + va_start(args, format); + + str = g_strdup_vprintf(format, args); + bot_send_cmd(bot, str); + g_free(str); + + va_end(args); +} + +/* broadcast a message to everyone in bot network, except for `except_bot' + if it's not NULL */ +void botnet_broadcast(BOTNET_REC *botnet, BOT_REC *except_bot, + const char *source, const char *data) +{ + GNode *node; + char *str; + + g_return_if_fail(botnet != NULL); + g_return_if_fail(data != NULL); + + str = g_strdup_printf("%s - %s", source != NULL ? source : + botnet->nick, data); + for (node = botnet->bots->children; node != NULL; node = node->next) { + BOT_REC *rec = node->data; + + if (rec != except_bot && rec->handle != -1) + bot_send_cmd(rec, str); + } + g_free(str); +} + +BOTNET_REC *botnet_find(const char *name) +{ + GSList *tmp; + + g_return_val_if_fail(name != NULL, NULL); + + for (tmp = botnets; tmp != NULL; tmp = tmp->next) { + BOTNET_REC *rec = tmp->data; + + if (g_strcasecmp(rec->name, name) == 0) + return rec; + } + + return NULL; +} + +typedef struct { + gconstpointer key; + int priority; + GNode *node; +} BOT_FIND_REC; + +static int gnode_find_nick(GNode *node, BOT_FIND_REC *rec) +{ + BOT_REC *bot = node->data; + + if (bot == NULL) return FALSE; + + if (bot->nick != NULL && g_strcasecmp(bot->nick, rec->key) == 0) { + rec->node = node; + return TRUE; + } + + return FALSE; +} + +GNode *bot_find_nick(BOTNET_REC *botnet, const char *nick) +{ + BOT_FIND_REC rec; + + g_return_val_if_fail(botnet != NULL, NULL); + g_return_val_if_fail(nick != NULL, NULL); + + rec.key = nick; + rec.node = NULL; + g_node_traverse(botnet->bots, 0, G_TRAVERSE_ALL, -1, + (GNodeTraverseFunc) gnode_find_nick, &rec); + return rec.node; +} + +/* Return the bot who we should send the message if we wanted `nick' to get it. */ +GNode *bot_find_path(BOTNET_REC *botnet, const char *nick) +{ + BOT_FIND_REC rec; + GNode *node; + + g_return_val_if_fail(botnet != NULL, NULL); + g_return_val_if_fail(nick != NULL, NULL); + + rec.key = nick; + rec.node = NULL; + for (node = botnet->bots->children; node != NULL; node = node->next) { + g_node_traverse(node, 0, G_TRAVERSE_ALL, -1, + (GNodeTraverseFunc) gnode_find_nick, &rec); + if (rec.node != NULL) return node; + } + return rec.node; +} + +/* check if `addr' is an IP address - this is checked to make sure that + if we have an address like "192.168.0.*", it wouldn't match to host name + 192.168.0.host.org */ +static int is_ip_mask(const char *addr) +{ + while (*addr != '\0') { + if (!isdigit(*addr) && *addr != '.' && + *addr != '*' && *addr != '?') return FALSE; + addr++; + } + + return TRUE; +} + +BOT_DOWNLINK_REC *bot_downlink_find(BOTNET_REC *botnet, IPADDR *ip, const char *host) +{ + GSList *tmp, *tmp2; + char ipname[MAX_IP_LEN]; + + g_return_val_if_fail(botnet != NULL, NULL); + g_return_val_if_fail(ip != NULL, NULL); + + net_ip2host(ip, ipname); + + for (tmp = botnet->downlinks; tmp != NULL; tmp = tmp->next) { + BOT_DOWNLINK_REC *rec = tmp->data; + + for (tmp2 = rec->valid_addrs; tmp2 != NULL; tmp2 = tmp2->next) { + if (match_wildcards(tmp2->data, ipname)) + return rec; + if (match_wildcards(tmp2->data, host) && + !is_ip_mask(tmp2->data)) + return rec; + } + } + + return NULL; +} + +static int gnode_find_master(GNode *node, BOT_FIND_REC *rec) +{ + BOT_REC *bot = node->data; + + if (bot == NULL) return FALSE; + + if (!bot->disconnect && bot->priority > rec->priority) { + rec->node = node; + return TRUE; + } + + return FALSE; +} + +BOT_REC *botnet_find_master(BOTNET_REC *botnet, BOT_REC *old_master) +{ + BOT_FIND_REC rec; + + g_return_val_if_fail(botnet != NULL, NULL); + + rec.node = NULL; + rec.priority = old_master == NULL ? -1 : old_master->priority; + g_node_traverse(botnet->bots, 0, G_TRAVERSE_ALL, -1, + (GNodeTraverseFunc) gnode_find_master, &rec); + return rec.node == NULL ? old_master : rec.node->data; +} + +void botnet_set_master(BOTNET_REC *botnet, BOT_REC *bot) +{ + g_return_if_fail(botnet != NULL); + g_return_if_fail(bot != NULL); + + if (botnet->master != NULL) + botnet->master->master = FALSE; + + bot->master = TRUE; + botnet->master = bot; +} + +void bot_nick_destroy(BOT_CHANNEL_REC *rec, NICK_REC *nick) +{ + g_return_if_fail(rec != NULL); + g_return_if_fail(nick != NULL); + + rec->nicks = g_slist_remove(rec->nicks, nick); + + g_free(nick->nick); + g_free_not_null(nick->realname); + g_free_not_null(nick->host); + g_free(nick); +} + +void bot_channel_destroy(BOT_IRCNET_REC *ircnet, BOT_CHANNEL_REC *rec) +{ + g_return_if_fail(ircnet != NULL); + g_return_if_fail(rec != NULL); + + ircnet->channels = g_slist_remove(ircnet->channels, rec); + + while (rec->nicks != NULL) + bot_nick_destroy(rec, rec->nicks->data); + + g_slist_foreach(rec->banlist, (GFunc) g_free, NULL); + g_slist_foreach(rec->ebanlist, (GFunc) g_free, NULL); + g_slist_foreach(rec->invitelist, (GFunc) g_free, NULL); + + g_slist_free(rec->banlist); + g_slist_free(rec->ebanlist); + g_slist_free(rec->invitelist); + + g_free_not_null(rec->mode); + g_free_not_null(rec->key); + g_free(rec->name); + g_free(rec); +} + +void bot_ircnet_destroy(BOT_REC *bot, BOT_IRCNET_REC *rec) +{ + g_return_if_fail(bot != NULL); + g_return_if_fail(rec != NULL); + + bot->ircnets = g_slist_remove(bot->ircnets, bot); + + while (rec->channels != NULL) + bot_channel_destroy(rec, rec->channels->data); + + g_free(rec->tag); + g_free(rec->ircnet); + g_free(rec->server); + g_free(rec->nick); + g_free(rec); +} + +void bot_disconnect(BOT_REC *bot) +{ + bot->disconnect = TRUE; + + signal_emit("bot disconnected", 1, bot); + + if (bot->read_tag != -1) { + g_source_remove(bot->read_tag); + bot->read_tag = -1; + } + if (bot->handle != -1) { + net_disconnect(bot->handle); + bot->handle = -1; + } +} + +static void bot_mark_disconnect(GNode *node) +{ + BOT_REC *bot = node->data; + + bot->disconnect = TRUE; +} + +#define bot_mark_disconnects(node) \ + g_node_traverse(node, G_LEVEL_ORDER, G_TRAVERSE_ALL, -1, \ + (GNodeTraverseFunc) bot_mark_disconnect, NULL) + +void bot_destroy(BOT_REC *bot) +{ + GNode *node; + + g_return_if_fail(bot != NULL); + + node = g_node_find(bot->botnet->bots, 0, G_TRAVERSE_ALL, bot); + if (node != NULL) { + if (!bot->disconnect) + bot_mark_disconnects(node); + } + + bot_disconnect(bot); + + if (node != NULL) { + while (node->children != NULL) + bot_destroy(node->children->data); + g_node_destroy(node); + } + + if (bot->botnet->uplink == bot) + bot->botnet->uplink = NULL; + if (bot->botnet->master == bot) + bot->botnet->master = NULL; + + while (bot->ircnets != NULL) + bot_ircnet_destroy(bot, bot->ircnets->data); + + line_split_free(bot->buffer); + g_free_not_null(bot->nick); + g_free(bot); +} + +void bot_downlink_destroy(BOT_DOWNLINK_REC *rec) +{ + rec->botnet->downlinks = g_slist_remove(rec->botnet->downlinks, rec); + + g_slist_foreach(rec->valid_addrs, (GFunc) g_free, NULL); + g_slist_free(rec->valid_addrs); + + g_free_not_null(rec->password); + g_free(rec); +} + +void bot_uplink_destroy(BOT_UPLINK_REC *rec) +{ + rec->botnet->uplinks = g_slist_remove(rec->botnet->uplinks, rec); + + g_free(rec->host); + g_free_not_null(rec->password); + g_free(rec); +} + +void botnet_disconnect(BOTNET_REC *botnet) +{ + botnet->connected = FALSE; + + bot_destroy(botnet->bots->data); + botnet->bots = NULL; + + if (botnet->listen_tag != -1) { + g_source_remove(botnet->listen_tag); + botnet->listen_tag = -1; + } + if (botnet->listen_handle != -1) { + net_disconnect(botnet->listen_handle); + botnet->listen_handle = -1; + } +} + +static void botnet_destroy(BOTNET_REC *botnet) +{ + botnets = g_slist_remove(botnets, botnet); + + while (botnet->uplinks != NULL) + bot_uplink_destroy(botnet->uplinks->data); + while (botnet->downlinks != NULL) + bot_downlink_destroy(botnet->downlinks->data); + + botnet_disconnect(botnet); + + g_free_not_null(botnet->addr); + g_free(botnet->name); + g_free(botnet->nick); + g_free(botnet); +} + +static void botnet_event(BOT_REC *bot, const char *data) +{ + char *params, *source, *target, *command, *args, *event; + + if (!bot->connected) + return; + + params = cmd_get_params(data, 4 | PARAM_FLAG_GETREST, + &source, &target, &command, &args); + + if (*target == '-' && target[1] == '\0') + target = NULL; + g_strdown(command); + + event = g_strconcat("botnet event ", command, NULL); + signal_emit(event, 4, bot, args, source, target); + g_free(event); + + g_free(params); +} + +static void botnet_event_bcast(BOT_REC *bot, const char *data, const char *sender) +{ + char *str; + + /* broadcast message to all bots */ + str = g_strdup_printf("BCAST %s", data); + botnet_broadcast(bot->botnet, bot, sender, str); + g_free(str); +} + +static void botnet_event_master(BOT_REC *bot, const char *data, const char *sender) +{ + BOTNET_REC *botnet; + BOT_REC *master; + GNode *node; + char *str; + + botnet = bot->botnet; + + node = bot_find_nick(bot->botnet, data); + master = node == NULL ? NULL : node->data; + master = botnet_find_master(bot->botnet, master); + g_return_if_fail(master != NULL); + + if (node == NULL || node->data != master) { + /* no, we don't agree with that master - + send our own to everyone. */ + bot = NULL; + } + + botnet_set_master(botnet, master); + + str = g_strdup_printf("MASTER %s", master->nick); + botnet_broadcast(botnet, bot, sender, str); + g_free(str); +} + +static void botnet_config_read_ips(BOT_DOWNLINK_REC *rec, CONFIG_NODE *node) +{ + GSList *tmp; + + g_return_if_fail(rec != NULL); + g_return_if_fail(node != NULL); + + node = config_node_section(node, "valid_addrs", -1); + tmp = node == NULL ? NULL : node->value; + for (; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + rec->valid_addrs = g_slist_append(rec->valid_addrs, g_strdup(node->value)); + } +} + +static void botnet_config_read_uplink(BOTNET_REC *botnet, CONFIG_NODE *node) +{ + BOT_UPLINK_REC *rec; + char *value; + + g_return_if_fail(botnet != NULL); + g_return_if_fail(node != NULL); + + value = config_node_get_str(node, "host", NULL); + if (value == NULL) return; /* host required */ + + rec = g_new0(BOT_UPLINK_REC, 1); + rec->botnet = botnet; + rec->host = g_strdup(value); + rec->port = config_node_get_int(node, "port", DEFAULT_BOTNET_PORT); + rec->password = g_strdup(config_node_get_str(node, "password", NULL)); + + botnet->uplinks = g_slist_append(botnet->uplinks, rec); +} + +static void botnet_config_read_downlink(BOTNET_REC *botnet, CONFIG_NODE *node) +{ + BOT_DOWNLINK_REC *rec; + + g_return_if_fail(botnet != NULL); + g_return_if_fail(node != NULL); + + rec = g_new0(BOT_DOWNLINK_REC, 1); + + botnet_config_read_ips(rec, node); + if (rec->valid_addrs == NULL) { + g_free(rec); + return; + } + + rec->botnet = botnet; + rec->password = g_strdup(config_node_get_str(node, "password", NULL)); + botnet->downlinks = g_slist_append(botnet->downlinks, rec); +} + +static void botnet_config_read_botnet(CONFIG_NODE *node) +{ + CONFIG_NODE *subnode; + BOTNET_REC *botnet; + GSList *tmp; + + g_return_if_fail(node != NULL); + + if (node->key == NULL || node->value == NULL) + return; + + /* New botnet */ + botnet = g_new0(BOTNET_REC, 1); + botnet->name = g_strdup(node->key); + botnet->nick = g_strdup(config_node_get_str(node, "nick", "bot")); + botnet->priority = config_node_get_int(node, "priority", DEFAULT_BOTNET_PRIORITY); + botnet->autoconnect = config_node_get_bool(node, "autoconnect", FALSE); + + botnet->addr = g_strdup(config_node_get_str(node, "listen_addr", NULL)); + botnet->port = config_node_get_int(node, "listen_port", DEFAULT_BOTNET_PORT); + + botnet->listen_handle = -1; + botnet->listen_tag = -1; + + /* read uplinks */ + subnode = config_node_section(node, "uplinks", -1); + tmp = subnode == NULL ? NULL : subnode->value; + for (; tmp != NULL; tmp = tmp->next) + botnet_config_read_uplink(botnet, tmp->data); + + /* read downlinks */ + subnode = config_node_section(node, "downlinks", -1); + tmp = subnode == NULL ? NULL : subnode->value; + for (; tmp != NULL; tmp = tmp->next) + botnet_config_read_downlink(botnet, tmp->data); + + botnets = g_slist_append(botnets, botnet); +} + +static void botnet_config_read(void) +{ + CONFIG_REC *config; + CONFIG_NODE *node; + GSList *tmp; + char *fname; + + /* Read botnets from ~/.irssi/botnets */ + fname = g_strdup_printf("%s/.irssi/botnets", g_get_home_dir()); + config = config_open(fname, -1); + g_free(fname); + + if (config == NULL) + return; + + config_parse(config); + + node = config_node_traverse(config, "botnets", FALSE); + tmp = node == NULL ? NULL : node->value; + for (; tmp != NULL; tmp = tmp->next) + botnet_config_read_botnet(tmp->data); + config_close(config); +} + +/* FIXME: this command is just temporary */ +static void cmd_botnet(const char *data) +{ + BOTNET_REC *botnet; + char *str; + + botnet = botnets->data; + + str = g_strdup_printf("BCAST %s", data); + botnet_broadcast(botnet, NULL, NULL, str); + g_free(str); +} + +static void autoconnect_botnets(void) +{ + GSList *tmp; + + for (tmp = botnets; tmp != NULL; tmp = tmp->next) { + BOTNET_REC *rec = tmp->data; + + if (rec->autoconnect) + botnet_connect(rec->name); + } +} + +void botnet_init(void) +{ + botnet_config_read(); + botnet_connection_init(); + + signal_add("botnet event", (SIGNAL_FUNC) botnet_event); + signal_add("botnet event bcast", (SIGNAL_FUNC) botnet_event_bcast); + signal_add("botnet event master", (SIGNAL_FUNC) botnet_event_master); + command_bind("botnet", NULL, (SIGNAL_FUNC) cmd_botnet); + + autoconnect_botnets(); +} + +void botnet_deinit(void) +{ + while (botnets) + botnet_destroy(botnets->data); + + botnet_connection_deinit(); + + signal_remove("botnet event", (SIGNAL_FUNC) botnet_event); + signal_remove("botnet event bcast", (SIGNAL_FUNC) botnet_event_bcast); + signal_remove("botnet event master", (SIGNAL_FUNC) botnet_event_master); + command_unbind("botnet", (SIGNAL_FUNC) cmd_botnet); +} |