forked from PsychoticNinja/irssi
Changed configure.in's functionality so that you could tell what modules you want to build in main irssi binary and it will create automatically the .c files that need to call the module_init()/deinit() functions. Fixed several minor things.. git-svn-id: http://svn.irssi.org/repos/irssi/trunk@230 dbcabf3a-b0e7-0310-adc4-f8d773084564
623 lines
14 KiB
C
623 lines
14 KiB
C
/*
|
|
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);
|
|
}
|