1
0
mirror of https://github.com/irssi/irssi.git synced 2024-09-29 04:45:57 -04:00

Added bot plugin, it also has almost-functional botnet.

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
This commit is contained in:
Timo Sirainen 2000-05-25 11:30:47 +00:00 committed by cras
parent 487da41745
commit 76605ad0ae
41 changed files with 2784 additions and 221 deletions

View File

@ -296,8 +296,29 @@ for c in $CHAT_MODULES; do
FE_COMMON_LIBS="$FE_COMMON_LIBS ../fe-common/$c/libfe_common_$c.la"
for s in `eval echo \\$${c}_MODULES`; do
CHAT_LIBS="$CHAT_LIBS ../$c/$s/lib${c}_$s.la"
FE_COMMON_LIBS="$FE_COMMON_LIBS ../fe-common/$c/$s/libfe_common_${c}_$s.la"
module_inits="$module_inits ${c}_${s}_init();"
module_deinits="${c}_${s}_deinit(); $module_deinits"
if test -d $srcdir/src/fe-common/$c/$s; then
FE_COMMON_LIBS="$FE_COMMON_LIBS ../fe-common/$c/$s/libfe_common_${c}_$s.la"
fe_module_inits="$fe_module_inits fe_${c}_${s}_init();"
fe_module_deinits="fe_${c}_${s}_deinit(); $fe_module_deinits"
fi
done
file="$srcdir/src/$c/$c.c"
echo "/* this file is automatically generated by configure - don't change */" > $file
echo "void ${c}_core_init(void); void ${c}_core_deinit(void);" >> $file
echo "$module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file
echo "$module_deinits" | $sedpath -e 's/[ ]*$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file
echo "void ${c}_init(void) { ${c}_core_init(); $module_inits }" >> $file
echo "void ${c}_deinit(void) { $module_deinits ${c}_core_deinit(); }" >> $file
file="$srcdir/src/fe-common/$c/${c}-modules.c"
echo "/* this file is automatically generated by configure - don't change */" > $file
echo "$fe_module_inits" | $sedpath -e 's/()/(void)/g' -e 's/ /void /g' >> $file
echo "$fe_module_deinits" | $sedpath -e 's/[ ]*$//' -e 's/()/(void)/g' -e 's/ /void /g' -e 's/^/void /' >> $file
echo "void fe_${c}_modules_init(void) { $fe_module_inits }" >> $file
echo "void fe_${c}_modules_deinit(void) { $fe_module_deinits }" >> $file
done
dnl ** common libraries needed by frontends
@ -338,6 +359,7 @@ src/Makefile
src/core/Makefile
src/irc/Makefile
src/irc/core/Makefile
src/irc/bot/Makefile
src/irc/dcc/Makefile
src/irc/notifylist/Makefile
src/irc/flood/Makefile

View File

@ -1,163 +1,316 @@
HISTORY
Irssi's botnet description
Copyright (c) 1999-2000 Timo Sirainen
0. History
draft v0.1 : 21.8.1999
Just a first draft of my botnet design I did on a boring friday
work afternoon :) I'll try to implement this to irssi some day, it
feels pretty interesting now so it might be pretty soon even. Any
comments are welcome :)
Just a first draft of my botnet design I did on a boring friday
work afternoon :) I'll try to implement this to irssi some day, it
feels pretty interesting now so it might be pretty soon even. Any
comments are welcome :)
draft v0.2 : 21.11.1999
Exactly three months since the first draft :) Now I actually have
some code done, just committed pretty simple botnet to irssi CVS.
Made several changes to this document.. Still missing much details
but the basic idea should be clear.
Exactly three months since the first draft :) Now I actually have
some code done, just committed pretty simple botnet to irssi CVS.
Made several changes to this document.. Still missing much details
but the basic idea should be clear.
------
draft v0.3 : 21.05.2000
A small description of what botnet would do: A group of bots
efficiently working together to perform their tasks. Like when
someone's trying to take over your channel, bots will quickly decide
who deops/kicks whom instead of multiple bots deopping or trying to
kick the same people..
Strange, again the same day. I really didn't plan this :)
Reformatted the text, added lots of text, implemented more of the
stuff.
config file:
mybotnet =
{
priority=n;
nick=mybot;
bots=
(
{ host="another.org"; port=5567; password="blah";
valid_addrs=("*.another.org"); },
{ host="blah.ircnetthing.net"; password="blah";
valid_addrs=("*.ircnetthing.net", "*.blah.org"); },
{ host="some.thing.net"; password="blah";
valid_addrs=("some.thing.net"); }
);
}
1. General
When connecting to botnet, it first tries to connect to the first bot
in bots list, then the second, etc. Setting port to 0 will prevent
connecting to the bot.
1.1 Description
Login:
A small description of what botnet would do: A group of bots
efficiently working together to perform their tasks. Like when
someone's trying to take over your channel, bots will quickly
decide who deops/kicks whom instead of multiple bots deopping or
trying to kick the same people.
First host checks what client is connecting from bots' valid_addrs. If
there's no matches it just disconnects the client.
Irssi's botnet is pretty much based on trust. Some malicious bot
can quite well mess up the whole botnet. Connecting the bots to
each other via ssh would be a good idea.
CLIENT: PASS blah
HOST : (if error, disconnect)
CLIENT: NICK nick
HOST : (if nick already in use) NICKERROR
CLIENT: PRIORITY=n
HOST : MASTER nick
HOST : CONNECTED
1.2 Configuration
Then host sends a list of all connected bots in botnet:
BOTINFO nick connected-to-nick address priority
example config file:
Now we're connected to botnet, rest of the commands will be send to
everyone. Commands are the following format (I won't write the nick
from now on):
mybotnet =
{
priority=5;
nick=mybot;
uplinks = (
{ host = "main.botnet.org"; password = "mypass"; },
{ host = "alter.botnet.org"; password = "pass2"; }
);
downlinks = (
{ password = "thepass"; valid_addrs = ( "192.168.0.*" ); },
{ password = "blah"; valid_addrs = ( "*.botnet.org" ); },
{ password = "localpass"; valid_addrs = ( "127.*" ); }
);
}
nick COMMAND [command specific data..]
When connecting to botnet, the bot first tries to connect to the
first bot in uplinks list, then the second, etc. Setting port to -1
will prevent connecting to the bot, 0 uses the default.
After connection is established with the client, host sends (except to
the connected client):
BOTNEW client_nick client_address client_priority
1.3 Botnet master
Master is the client with the highest priority, if there's multiple
with the same priority, the one who's nick is the first in alphabet
"wins" and says:
To avoid total chaos inside botnet, the bots shouldn't do (almost)
anything without a command from botnet's master. The master should
know everything, and give commands to clients that can perform the
task best.
MASTER
Master is the bot with the highest priority. If there's multiple
with the same priority, the one that already was the master will
stay master. When joining two botnets to one, the uplink's master
stays. If link to master breaks, the closest bot to it will choose
a new one.
Also after connecting, client could check if it's priority is bigger than
current master's and make itself the master.
The priorities should be given so that the bots that have the
fastest connections and are in the middle of the botnet have the
highest priorities.
Bots should every now and then check if their connections are active by
sending PING, the other side replies with PONG. If PONG isn't received
for a while (3 min?), the connection should be closed. If there's more
bots behind the lost bot, either side should try to reconnect to the
other one. Also if there's too much lag (>30sec) for some bots, their
priority could be temporarily lowered. When something urgent happens
and there's a lot of lag, each subset of bots should try to behave
independently.
1.4 Command format
When connection is closed to some bot, a notice is sent:
BOTQUIT nick
Commands that are sent inside botnet are in the following format:
After bot is (dis)connected to some irc network and is ready to take
commands, it sends notice:
BOTCONNECT ircnet (where ircnet is network's name, like IRCNet, EFNet, ..)
BOTDISCONNECT ircnet
<from_nick> <to_nick> COMMAND [command specific data..]
After joining/leaving channels, bot sends notice:
BOTJOIN ircnet #channel
BOTPART ircnet #channel
If to_nick is '-', the command should be sent to everyone.
After BOTJOIN, master tries to op the bot. When bot receives +o, it replies:
BOTOP ircnet #channel
If it's the first opped bot in channel, master orders the bot to op the rest
of the bots.
2. Handshake
Or after kicked or when being unable to join..:
BOTKICK ircnet #channel
BOTBANNED ircnet #channel
BOTCANTJOIN ircnet #channel
First host checks from bots' valid_addrs who is connecting. If
there's no matches it just disconnects the client.
When master notices that bot is kicked, it first checks if there's any other
opped bots in channel. If not, it waits for a random pause, 5-10sec before
joining (so it won't get autorejoin ban). When received BOTBANNED, master
tries to unban bot, BOTCANTJOIN results as invite to channel.
CLIENT: PASS <password>
HOST : (if error, disconnect)
When master notices that bot is the first one joined to channel, it asks bot
for channel information:
CLIENT: NICK <nick>
HOST : NICKERROR | CONNECTED
BOTCMD nick ircnet NAMES #channel
BOTCMD nick ircnet WHO #channel
BOTCMD nick ircnet MODE #channel
BOTCMD nick ircnet MODE b #channel
BOTCMD nick ircnet MODE e #channel
BOTCMD nick ircnet MODE I #channel
If nick is already in use, the host sends NICKERROR and waits for
new nick.
Next command is sent right after getting answer from the last query. It's also
possible that if several bots join immediately after the first bot, the
commands are shared between all the bots. After getting the results, the bot
replies:
Now we're connected to botnet. The commands from now on use the
format specified in section 1.4.
BOTREPLY ircnet <reply>
Both the client and the host sends information to other side of
all the clients they serve (if any):
Bots should cache the information as much as possible, at least NAMES command.
BOTINFO <nick> <connected_to_nick> <priority>
Every channel has a priority: LOW, NORMAL, HIGH.
BOTINFOs must be sent sorted so that connected_to_nick bot is
always known. Like first comes the bots connected to the
host/client, then the bots connected to them etc.
Normally LOW operates just as NORMAL channels, except when some channel
has HIGH priority and bots are really busy, LOW channels just wait
until there's time for them.
If the client had downlinks, nick collisions might happen. The
uplink is responsible for noticing them from BOTINFO commands.
It should automatically replace the nicks with new ones and
send nick change command to client and all it's downlinks. For
example if host received:
In NORMAL channels, the most urgent operations (kicks, ops, deops) are
performed quite soon even while bots are busy handling HIGH priority
commands.
BOTINFO bot highbot 10
Channels shouldn't normally be HIGH priority, but if attack against
channel is detected (like someone comes from split, gets ops and gets
to op someone else), it's priority is set to HIGH. When channel's
priority is HIGH, botnet does everything it can to get rid of
unauthorized opped people as fast as possible.
And the bot already exists, the BOTINFO is changed to:
LOW channel's priority can also be raised to HIGH, but it's priority is
dropped back to LOW if some NORMAL channel's priority is raised to HIGH
too.
BOTINFO bot2 highbot 10
Channel's priority can also be set manually:
CHPRIORITY ircnet #channel <LOW/NORMAL/HIGH>
And the client and it's downlinks are notified:
------
BOTNICK bot2 bot
After sending BOTINFOs, the host tells the current master:
MASTER <nick>
The client now checks if it's priority is higher than the current
master's. If it is, it will send the MASTER command without any
parameters.
3. Bot connections
3.1 General
Everyone's connections should be kept in n-way tree. Example:
[highuplink]
_____________/ | | \
/ | [h5] [h6]
[h1] | / | \
/ \ | [h7] | [h8]
[h2] [h3] | | \
| [uplink] [h9] [h10]
[h4] / | \
[up2] | [up1]
/ | | |
[up3] [up4] | [up5]
|
[we]
/ \
[client1] [client2]
/ \
[c3] [c4]
Botnet should try to keep together even if some hub in the middle
crashes. Each bot should have at least two uplinks in case one
dies. For example if [uplink] dies, [we] and [up1] could connect
to [up2], and [up2] could connect to [highuplink].
When connection is closed to some bot, a notice is sent by the
bot's uplink:
BOTQUIT <nick>
The quit notice should be sent only about the bot that was
disconnected. Bots should figure out themselves the other bots and
remove them too from their lists.
3.2 Lag
Each bot should send PING commands to their up/downlinks every
now and then (1min?) to check that the connection is still active.
If the PONG isn't received in 10 seconds, it's priority should be
temporarily lowered to -1. If the PONG isn't received in 3
minutes, the whole connection should be closed.
Master should know lag times of every bots. It could then
automatically raise/lower bots' priorities depending on how big
their lag is. Master could also lower it's own priority and pass
the master status to someone else with lower lag.
If there's lot of lag (>3sec?) somewhere and something urgent
happens, the botnet could split and behave independently.
4. IRC networks
4.1 Server connections
When bot is connected to some irc server and is ready to take
commands, it says:
IRCJOIN <tag> <ircnet> <server> <nick>
Tag is the bot specific unique tag of the server, so that the bot
can connect multiple times to same IRC network. All IRC related
commands should specify the server tag where it should be sent.
If bot quits an irc network, it says:
IRCQUIT <tag>
4.2 IRC commands
Master asks a bot to send some command to IRC server by saying:
CMD <id> <tag> <command>
<command> can't really be anything, since the bot should also be
able to reply to it. The <id> is for identifying the command/reply
pair. Master should keep the command in memory until it receives
the reply:
CMDREPLY <id> <last> <reply>
The command could get a reply of multiple lines, so <last>
specifies if the reply is the last line (1 or 0).
If the command failed for some reason, the bot will reply with
CMDFAIL <id>
and master should send the command to some other bot.
4.3 Channels
When joined/left channels, the bot says:
CHANJOIN <tag> <channel>
CHANPART <tag> <channel>
After BOTJOIN, master tries to op the bot. When bot receives +o,
it says:
CHANOP <tag> <channel>
If it is the first opped bot in channel, master orders the bot to
op the rest of the bots.
If the bot is kicked, it says:
CHANKICK <tag> <channel>
When master notices that bot is kicked, it first checks if there's
any other opped bots in channel. If not, it waits for a random
pause, 5-10sec before letting the bot join the channel again so
that it won't get autorejoin ban.
If bot can't join channel, it says:
CHANBANNED <tag> <channel>
(or)
CHANCANTJOIN <tag> <channel>
When received BOTBANNED, master tries to unban bot or set a ban
exception. BOTCANTJOIN results as invite to channel.
4.4 Channel information
When master notices that bot is the first one joined to channel,
it asks the bot for some channel information:
CMD <id> <tag> NAMES <channel>
CMD <id> <tag> WHO <channel>
CMD <id> <tag> MODE <channel>
CMD <id> <tag> MODE b <channel>
CMD <id> <tag> MODE e <channel> (if IRC network supports this)
CMD <id> <tag> MODE I <channel> (if IRC network supports this)
It's also possible that if several bots join immediately after the
first bot, the commands are shared between all the bots.
Bots should cache the information as much as possible, at least
NAMES command.
4.5 Channel priorities
Every channel has a priority: LOW, NORMAL, HIGH.
Normally LOW operates just as NORMAL channels, except when some
channel has HIGH priority and bots are really busy, LOW channels
just wait until there's time for them.
In NORMAL channels, the most urgent operations (kicks, ops, deops)
are performed quite soon even while bots are busy handling HIGH
priority commands.
Channels shouldn't normally be HIGH priority, but if attack
against channel is detected (like someone comes from split, gets
ops and gets to op someone else), it's priority is set to HIGH.
When channel's priority is HIGH, botnet does everything it can to
get rid of unauthorized opped people as fast as possible.
LOW channel's priority can also be raised to HIGH, but it's
priority is dropped back to LOW if some NORMAL channel's priority
is raised to HIGH too.
Master notifies about channel's priority change by saying:
CHANPRIORITY <ircnet> <channel> <LOW/NORMAL/HIGH>
Copyright (c) 1999 Timo Sirainen

View File

@ -409,7 +409,7 @@ int match_wildcards(const char *cmask, const char *data)
}
ret = data != NULL && *data == '\0' && *mask == '\0';
free(newmask);
g_free(newmask);
return ret;
}

View File

@ -39,7 +39,7 @@ SIMPLE_THREAD_REC;
/* nonblocking gethostbyname(), ip (IPADDR) + error (int, 0 = not error) is
written to pipe when found PID of the resolver child is returned */
int net_gethostname_nonblock(const char *addr, int pipe)
int net_gethostbyname_nonblock(const char *addr, int pipe)
{
RESOLVED_IP_REC rec;
const char *errorstr;
@ -60,7 +60,8 @@ int net_gethostname_nonblock(const char *addr, int pipe)
}
/* child */
rec.error = net_gethostname(addr, &rec.ip);
memset(&rec, 0, sizeof(rec));
rec.error = net_gethostbyname(addr, &rec.ip);
if (rec.error == 0) {
errorstr = NULL;
} else {
@ -105,7 +106,7 @@ int net_gethostbyname_return(int pipe, RESOLVED_IP_REC *rec)
if (rec->error) {
/* read error string */
rec->errorstr = g_malloc(rec->errlen);
rec->errorstr = g_malloc(rec->errlen+1);
len = 0;
do {
ret = read(pipe, rec->errorstr+len, rec->errlen-len);
@ -122,6 +123,13 @@ int net_gethostbyname_return(int pipe, RESOLVED_IP_REC *rec)
return 0;
}
/* Get host name, call func when finished */
int net_gethostbyaddr_nonblock(IPADDR *ip, NET_HOST_CALLBACK func, void *data)
{
/*FIXME*/
return FALSE;
}
/* Kill the resolver child */
void net_disconnect_nonblock(int pid)
{
@ -173,7 +181,7 @@ static void simple_readpipe(SIMPLE_THREAD_REC *rec, int pipe)
return;
}
rec->tag = g_input_add(handle, G_INPUT_WRITE,
rec->tag = g_input_add(handle, G_INPUT_READ | G_INPUT_WRITE,
(GInputFunction) simple_init, rec);
}
@ -192,7 +200,7 @@ int net_connect_nonblock(const char *server, int port, const IPADDR *my_ip, NET_
}
/* start nonblocking host name lookup */
net_gethostname_nonblock(server, fd[1]);
net_gethostbyname_nonblock(server, fd[1]);
rec = g_new0(SIMPLE_THREAD_REC, 1);
rec->port = port;

View File

@ -11,10 +11,22 @@ typedef struct {
need to free() it yourself unless it's NULL */
} RESOLVED_IP_REC;
typedef struct {
int namelen;
char *name;
int error;
int errlen;
char *errorstr;
} RESOLVED_NAME_REC;
typedef void (*NET_CALLBACK) (int, void *);
typedef void (*NET_HOST_CALLBACK) (RESOLVED_NAME_REC *, void *);
/* nonblocking gethostbyname(), PID of the resolver child is returned. */
int net_gethostname_nonblock(const char *addr, int pipe);
int net_gethostbyname_nonblock(const char *addr, int pipe);
/* Get host's name, call func when finished */
int net_gethostbyaddr_nonblock(IPADDR *ip, NET_HOST_CALLBACK func, void *data);
/* get the resolved IP address. returns -1 if some error occured with read() */
int net_gethostbyname_return(int pipe, RESOLVED_IP_REC *rec);

View File

@ -50,7 +50,18 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
/* copy IP to sockaddr */
inline void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
{
so->sin.sin_family = ip->family;
if (ip == NULL) {
#ifdef HAVE_IPV6
so->sin6.sin6_family = AF_INET6;
so->sin6.sin6_addr.s_addr = in6addr_any;
#else
so->sin.sin_family = AF_INET;
so->sin.sin_addr.s_addr = INADDR_ANY;
#endif
return;
}
so->sin.sin_family = ip->family;
#ifdef HAVE_IPV6
if (ip->family == AF_INET6)
memcpy(&so->sin6.sin6_addr, &ip->addr, sizeof(ip->addr.ip6));
@ -97,7 +108,7 @@ int net_connect(const char *addr, int port, IPADDR *my_ip)
g_return_val_if_fail(addr != NULL, -1);
if (net_gethostname(addr, &ip) == -1)
if (net_gethostbyname(addr, &ip) == -1)
return -1;
return net_connect_ip(&ip, port, my_ip);
@ -149,21 +160,22 @@ void net_disconnect(int handle)
close(handle);
}
/* Listen for connections on a socket */
/* Listen for connections on a socket. if `my_ip' is NULL, listen in any
address. */
int net_listen(IPADDR *my_ip, int *port)
{
union sockaddr_union so;
int ret, handle, opt = 1;
socklen_t len = sizeof(so);
g_return_val_if_fail(my_ip != NULL, -1);
g_return_val_if_fail(port != NULL, -1);
/* create the socket */
memset(&so, 0, sizeof(so));
so.sin.sin_family = my_ip->family;
handle = socket(my_ip->family, SOCK_STREAM, 0);
sin_set_port(&so, *port);
sin_set_ip(&so, my_ip);
/* create the socket */
handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
if (handle == -1)
return -1;
@ -173,7 +185,6 @@ int net_listen(IPADDR *my_ip, int *port)
setsockopt(handle, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt));
/* specify the address/port we want to listen in */
sin_set_port(&so, *port);
ret = bind(handle, &so.sa, sizeof(so));
if (ret < 0) {
close(handle);
@ -190,8 +201,7 @@ int net_listen(IPADDR *my_ip, int *port)
*port = sin_get_port(&so);
/* start listening */
if (listen(handle, 1) < 0)
{
if (listen(handle, 1) < 0) {
close(handle);
return -1;
}
@ -207,8 +217,6 @@ int net_accept(int handle, IPADDR *addr, int *port)
socklen_t addrlen;
g_return_val_if_fail(handle != -1, -1);
g_return_val_if_fail(addr != NULL, -1);
g_return_val_if_fail(port != NULL, -1);
addrlen = sizeof(so);
ret = accept(handle, &so.sa, &addrlen);
@ -216,8 +224,8 @@ int net_accept(int handle, IPADDR *addr, int *port)
if (ret < 0)
return -1;
sin_get_ip(&so, addr);
*port = sin_get_port(&so);
if (addr != NULL) sin_get_ip(&so, addr);
if (port != NULL) *port = sin_get_port(&so);
fcntl(ret, F_SETFL, O_NONBLOCK);
return ret;
@ -297,7 +305,7 @@ int net_getsockname(int handle, IPADDR *addr, int *port)
/* Get IP address for host, returns 0 = ok,
others = error code for net_gethosterror() */
int net_gethostname(const char *addr, IPADDR *ip)
int net_gethostbyname(const char *addr, IPADDR *ip)
{
#ifdef HAVE_IPV6
union sockaddr_union *so;
@ -310,7 +318,6 @@ int net_gethostname(const char *addr, IPADDR *ip)
g_return_val_if_fail(addr != NULL, -1);
/* host name */
#ifdef HAVE_IPV6
memset(ip, 0, sizeof(IPADDR));
memset(&req, 0, sizeof(struct addrinfo));
@ -338,6 +345,50 @@ int net_gethostname(const char *addr, IPADDR *ip)
return 0;
}
/* Get name for host, *name should be g_free()'d unless it's NULL.
Return values are the same as with net_gethostbyname() */
int net_gethostbyaddr(IPADDR *ip, char **name)
{
#ifdef HAVE_IPV6
struct addrinfo req, *ai;
char hbuf[NI_MAXHOST];
int host_error;
#else
struct hostent *hp;
#endif
char ipname[MAX_IP_LEN];
g_return_val_if_fail(ip != NULL, -1);
g_return_val_if_fail(name != NULL, -1);
*name = NULL;
#ifdef HAVE_IPV6
memset(&req, 0, sizeof(struct addrinfo));
req.ai_socktype = SOCK_STREAM;
/* save error to host_error for later use */
host_error = getaddrinfo(addr, NULL, &req, &ai);
if (host_error != 0)
return host_error;
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, 0))
return 1;
/*FIXME: how does this work? *name = g_strdup(ai->???);*/
freeaddrinfo(ai);
return 1;
#else
net_ip2host(ip, ipname);
hp = gethostbyaddr(ipname, strlen(ipname), AF_INET);
if (hp == NULL) return -1;
*name = g_strdup(hp->h_name);
#endif
return 0;
}
int net_ip2host(IPADDR *ip, char *host)
{
#ifdef HAVE_IPV6

View File

@ -52,7 +52,10 @@ int net_transmit(int handle, const char *data, int len);
/* Get IP address for host, returns 0 = ok,
others = error code for net_gethosterror() */
int net_gethostname(const char *addr, IPADDR *ip);
int net_gethostbyname(const char *addr, IPADDR *ip);
/* Get name for host, *name should be g_free()'d unless it's NULL.
Return values are the same as with net_gethostbyname() */
int net_gethostbyaddr(IPADDR *ip, char **name);
/* get error of net_gethostname() */
const char *net_gethosterror(int error);

View File

@ -175,9 +175,9 @@ int server_connect(SERVER_REC *server)
server->handle = -1;
server->connect_pid =
net_gethostname_nonblock(server->connrec->proxy != NULL ?
server->connrec->proxy : server->connrec->address,
server->connect_pipe[1]);
net_gethostbyname_nonblock(server->connrec->proxy != NULL ?
server->connrec->proxy : server->connrec->address,
server->connect_pipe[1]);
server->connect_tag =
g_input_add(server->connect_pipe[0], G_INPUT_READ,

View File

@ -25,6 +25,7 @@ libfe_common_irc_la_SOURCES = \
fe-common-irc.c \
irc-window-activity.c \
irc-hilight-text.c \
irc-modules.c \
module-formats.c
noinst_HEADERS = \

View File

@ -403,7 +403,7 @@ static void dcc_chat_closed(WINDOW_REC *window, WI_IRC_REC *item)
}
}
void fe_dcc_init(void)
void fe_irc_dcc_init(void)
{
signal_add("dcc connected", (SIGNAL_FUNC) dcc_connected);
signal_add("dcc rejected", (SIGNAL_FUNC) dcc_rejected);
@ -432,7 +432,7 @@ void fe_dcc_init(void)
theme_register(fecommon_irc_dcc_formats);
}
void fe_dcc_deinit(void)
void fe_irc_dcc_deinit(void)
{
theme_unregister();

View File

@ -31,6 +31,9 @@
#include "themes.h"
#include "completion.h"
void fe_irc_modules_init(void);
void fe_irc_modules_deinit(void);
void fe_channels_init(void);
void fe_channels_deinit(void);
@ -43,9 +46,6 @@ void fe_irc_server_deinit(void);
void fe_ctcp_init(void);
void fe_ctcp_deinit(void);
void fe_dcc_init(void);
void fe_dcc_deinit(void);
void fe_events_init(void);
void fe_events_deinit(void);
@ -61,12 +61,6 @@ void fe_query_deinit(void);
void irc_window_activity_init(void);
void irc_window_activity_deinit(void);
void fe_notifylist_init(void);
void fe_notifylist_deinit(void);
void fe_flood_init(void);
void fe_flood_deinit(void);
void fe_netsplit_init(void);
void fe_netsplit_deinit(void);
@ -108,30 +102,28 @@ void fe_common_irc_init(void)
fe_irc_commands_init();
fe_irc_server_init();
fe_ctcp_init();
fe_dcc_init();
fe_events_init();
fe_events_numeric_init();
fe_ignore_init();
fe_notifylist_init();
fe_flood_init();
fe_netsplit_init();
fe_query_init();
completion_init();
irc_window_activity_init();
fe_irc_modules_init();
}
void fe_common_irc_deinit(void)
{
fe_irc_modules_deinit();
fe_channels_deinit();
fe_irc_commands_deinit();
fe_irc_server_deinit();
fe_ctcp_deinit();
fe_dcc_deinit();
fe_events_deinit();
fe_events_numeric_deinit();
fe_ignore_deinit();
fe_notifylist_deinit();
fe_flood_deinit();
fe_netsplit_deinit();
fe_query_deinit();
completion_deinit();

View File

@ -43,7 +43,7 @@ static void event_autoignore_remove(IRC_SERVER_REC *server, AUTOIGNORE_REC *igno
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_AUTOUNIGNORE, ignore->nick);
}
void fe_flood_init(void)
void fe_irc_flood_init(void)
{
signal_add("autoignore new", (SIGNAL_FUNC) event_autoignore_new);
signal_add("autoignore remove", (SIGNAL_FUNC) event_autoignore_remove);
@ -51,7 +51,7 @@ void fe_flood_init(void)
theme_register(fecommon_irc_flood_formats);
}
void fe_flood_deinit(void)
void fe_irc_flood_deinit(void)
{
theme_unregister();

View File

@ -224,7 +224,7 @@ static void notifylist_unidle(IRC_SERVER_REC *server, const char *nick,
server->connrec->ircnet == NULL ? "IRC" : server->connrec->ircnet);
}
void fe_notifylist_init(void)
void fe_irc_notifylist_init(void)
{
theme_register(fecommon_irc_notifylist_formats);
@ -235,7 +235,7 @@ void fe_notifylist_init(void)
signal_add("notifylist unidle", (SIGNAL_FUNC) notifylist_unidle);
}
void fe_notifylist_deinit(void)
void fe_irc_notifylist_deinit(void)
{
theme_unregister();

View File

@ -22,7 +22,6 @@
#include "args.h"
#include "signals.h"
#include "core.h"
#include "irc-core.h"
void irc_init(void);
void irc_deinit(void);

View File

@ -24,7 +24,6 @@
#include "signals.h"
#include "core.h"
#include "irc-core.h"
#include "fe-common-core.h"
#include "fe-common-irc.h"
#include "themes.h"

View File

@ -1,4 +1,4 @@
SUBDIRS = core dcc flood notifylist
SUBDIRS = core bot dcc flood notifylist
noinst_LTLIBRARIES = libirc.la

8
src/irc/bot/.cvsignore Normal file
View File

@ -0,0 +1,8 @@
*.la
*.lo
*.o
.deps
.libs
Makefile
Makefile.in
so_locations

24
src/irc/bot/Makefile.am Normal file
View File

@ -0,0 +1,24 @@
plugindir = $(libdir)/irssi/plugins
plugin_LTLIBRARIES = libirc_bot.la
INCLUDES = $(GLIB_CFLAGS) \
-I$(top_srcdir)/src -I$(top_srcdir)/src/core/ -I$(top_srcdir)/src/irc/core/
libirc_bot_la_LIBADD = -lcrypt
libirc_bot_la_SOURCES = \
bot.c \
bot-commands.c \
bot-events.c \
bot-users.c \
botnet.c \
botnet-connection.c
noinst_HEADERS = \
bot.h \
botnet.h \
bot-users.h
EXTRA_DIST = \
users.sample \
botnets.sample

165
src/irc/bot/bot-commands.c Normal file
View File

@ -0,0 +1,165 @@
/*
bot-commands.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 "signals.h"
#include "commands.h"
#include "irc.h"
#include "irc-server.h"
#include "channels.h"
#include "nicklist.h"
#include "masks.h"
#include "bot-users.h"
static void event_privmsg(const char *data, IRC_SERVER_REC *server,
const char *nick, const char *address)
{
char *params, *target, *msg, *args, *str;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
if (nick == NULL) nick = server->real_address;
if (*msg == 1 || ischannel(*target)) {
g_free(params);
return;
}
/* private message for us */
str = g_strconcat("bot command ", msg, NULL);
args = strchr(str+12, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
g_strdown(str);
if (signal_emit(str, 4, args, server, nick, address)) {
/* msg was a command - the msg event. */
signal_stop();
}
g_free(str);
g_free(params);
}
static void botcmd_op(const char *data, IRC_SERVER_REC *server,
const char *nick, const char *address)
{
CHANNEL_REC *channel;
USER_REC *user;
USER_CHAN_REC *userchan;
GSList *tmp;
g_return_if_fail(data != NULL);
if (*data == '\0') {
/* no password given? .. */
return;
}
user = botuser_find(nick, address);
if (user == NULL || (user->not_flags & USER_OP) ||
!botuser_verify_password(user, data)) {
/* not found, can't op with this mask or failed password */
return;
}
/* find the channels where to op.. */
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
channel = tmp->data;
userchan = g_hash_table_lookup(user->channels, channel->name);
if ((user->flags & USER_OP) || (userchan->flags & USER_OP))
signal_emit("command op", 3, nick, server, channel);
}
}
static void botcmd_ident(const char *data, IRC_SERVER_REC *server,
const char *nick, const char *address)
{
USER_REC *user;
char *mask;
g_return_if_fail(data != NULL);
user = botuser_find(nick, address);
if (user != NULL) {
/* Already know this host */
return;
}
user = botuser_find(nick, NULL);
if (user == NULL || !botuser_verify_password(user, data)) {
/* failed password */
return;
}
/* add the new mask */
mask = irc_get_mask(nick, address, IRC_MASK_USER | IRC_MASK_DOMAIN);
botuser_add_mask(user, mask);
irc_send_cmdv(server, "NOTICE %s :Added new mask %s", nick, mask);
g_free(mask);
}
static void botcmd_pass(const char *data, IRC_SERVER_REC *server,
const char *nick, const char *address)
{
USER_REC *user;
char *params, *pass, *newpass;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, &pass, &newpass);
user = botuser_find(nick, address);
if (user == NULL || *pass == '\0') {
g_free(params);
return;
}
if (user->password != NULL &&
(*newpass == '\0' || !botuser_verify_password(user, pass))) {
g_free(params);
return;
}
/* change the password */
botuser_set_password(user, user->password == NULL ? pass : newpass);
irc_send_cmdv(server, "NOTICE %s :Password changed", nick);
g_free(params);
}
void bot_commands_init(void)
{
signal_add("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_add_last("bot command op", (SIGNAL_FUNC) botcmd_op);
signal_add_last("bot command ident", (SIGNAL_FUNC) botcmd_ident);
signal_add_last("bot command pass", (SIGNAL_FUNC) botcmd_pass);
}
void bot_commands_deinit(void)
{
signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_remove("bot command op", (SIGNAL_FUNC) botcmd_op);
signal_remove("bot command ident", (SIGNAL_FUNC) botcmd_ident);
signal_remove("bot command pass", (SIGNAL_FUNC) botcmd_pass);
}

199
src/irc/bot/bot-events.c Normal file
View File

@ -0,0 +1,199 @@
/*
bot-events.c : IRC bot plugin for irssi
Copyright (C) 1999 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 "signals.h"
#include "commands.h"
#include "irc.h"
#include "irc-server.h"
#include "channels.h"
#include "nicklist.h"
#include "modes.h"
#include "netsplit.h"
#include "bot-users.h"
static int get_flags(USER_REC *user, CHANNEL_REC *channel)
{
USER_CHAN_REC *userchan;
g_return_val_if_fail(user != NULL, 0);
g_return_val_if_fail(channel != NULL, 0);
userchan = g_hash_table_lookup(user->channels, channel->name);
return (user->flags | (userchan == NULL ? 0 : userchan->flags)) &
(~user->not_flags);
}
static void event_massjoin(CHANNEL_REC *channel, GSList *users)
{
USER_REC *user;
USER_CHAN_REC *userchan;
NICK_REC *nick;
GString *modestr, *nickstr;
int flags;
g_return_if_fail(channel != NULL);
g_return_if_fail(users != NULL);
modestr = g_string_new(NULL);
nickstr = g_string_new(NULL);
for (; users != NULL; users = users->next) {
user = users->data;
userchan = g_hash_table_lookup(user->channels, channel->name);
nick = userchan->nickrec;
flags = get_flags(user, channel);
if (!nick->op && (flags & USER_AUTO_OP)) {
g_string_sprintfa(modestr, "+o");
g_string_sprintfa(nickstr, "%s,", nick->nick);
}
if (!nick->voice && !nick->op && (flags & USER_AUTO_VOICE)) {
g_string_sprintfa(modestr, "+v");
g_string_sprintfa(nickstr, "%s,", nick->nick);
}
}
if (nickstr->len > 0) {
g_string_truncate(nickstr, nickstr->len-1);
g_string_sprintfa(modestr, " %s", nickstr->str);
channel_set_mode(channel->server, channel->name, modestr->str);
}
g_string_free(modestr, TRUE);
g_string_free(nickstr, TRUE);
}
/* Parse channel mode string */
static void parse_channel_mode(CHANNEL_REC *channel, const char *mode,
const char *nick, const char *address)
{
NICK_REC *nickrec, *splitnick;
USER_REC *user;
GString *str;
char *ptr, *curmode, type, *dup, *modestr;
int flags;
g_return_if_fail(channel != NULL);
g_return_if_fail(nick != NULL);
g_return_if_fail(modestr != NULL);
user = botuser_find(nick, address);
flags = user == NULL ? 0 : get_flags(user, channel);
if (!channel->chanop || (flags & USER_MASTER) ||
g_strcasecmp(nick, channel->server->nick) == 0) {
/* can't do anything or we/master did mode change,
don't bother checking what */
return;
}
str = g_string_new(NULL);
dup = modestr = g_strdup(mode);
type = '+';
curmode = cmd_get_param(&modestr);
for (; *curmode != '\0'; curmode++) {
if (*curmode == '+' || *curmode == '-') {
type = *curmode;
continue;
}
if (!HAS_MODE_ARG(*curmode))
ptr = NULL;
else {
ptr = cmd_get_param(&modestr);
if (*ptr == '\0') continue;
}
if (*curmode != 'o')
continue;
if (type == '-' && strcmp(channel->server->nick, ptr) == 0) {
/* we aren't chanop anymore .. */
g_string_truncate(str, 0);
break;
}
if (type != '+')
continue;
/* check that op is valid */
nickrec = nicklist_find(channel, ptr);
if (nickrec == NULL || nickrec->host == NULL)
continue;
user = botuser_find(ptr, nickrec->host);
flags = user == NULL ? 0 : get_flags(user, channel);
if (flags & USER_OP)
continue;
if (address == NULL) {
/* server opped, check if user was opped before netsplit. */
splitnick = netsplit_find_channel(channel->server, nickrec->nick, nickrec->host, channel->name);
if (splitnick != NULL && splitnick->op)
continue;
}
/* this one isn't supposed to get ops! */
g_string_sprintfa(str, "%s ", ptr);
}
g_free(dup);
if (str->len != 0)
signal_emit("command deop", 3, str->str, channel->server, channel);
g_string_free(str, TRUE);
}
static void event_mode(const char *data, IRC_SERVER_REC *server,
const char *nick, const char *address)
{
CHANNEL_REC *chanrec;
char *params, *channel, *mode;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &channel, &mode);
if (ischannel(*channel)) {
/* channel mode change */
chanrec = channel_find(server, channel);
if (chanrec != NULL)
parse_channel_mode(chanrec, mode, nick, address);
}
g_free(params);
}
void bot_events_init(void)
{
signal_add_last("bot massjoin", (SIGNAL_FUNC) event_massjoin);
signal_add("event mode", (SIGNAL_FUNC) event_mode);
}
void bot_events_deinit(void)
{
signal_remove("bot massjoin", (SIGNAL_FUNC) event_massjoin);
signal_remove("event mode", (SIGNAL_FUNC) event_mode);
}

503
src/irc/bot/bot-users.c Normal file
View File

@ -0,0 +1,503 @@
/*
bot-users.c : IRC bot plugin for irssi - user handling
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
*/
#define _XOPEN_SOURCE /* for crypt() */
#include "module.h"
#include "signals.h"
#include "misc.h"
#include "lib-config/iconfig.h"
#include "channels.h"
#include "nicklist.h"
#include "masks.h"
#include "bot-users.h"
#define WRITE_USERS_INTERVAL (60*15)
static char *user_flags = "oavm"; /* Keep these in the same order as USER_xxx flags */
static CONFIG_REC *userconfig;
static GHashTable *users;
static int writeusers_tag;
static time_t last_write;
int botuser_flags2value(const char *flags)
{
char *pos;
int val;
g_return_val_if_fail(flags != NULL, 0);
val = 0;
while (*flags != '\0') {
pos = strchr(user_flags, *flags);
if (pos != NULL)
val |= 1 << (int) (pos-user_flags);
flags++;
}
return val;
}
char *botuser_value2flags(int value)
{
char *str, *p;
int n;
p = str = g_malloc(USER_FLAG_COUNT+1);
for (n = 0; n < USER_FLAG_COUNT; n++) {
if (value & (1 << n))
*p++ = user_flags[n];
}
*p = '\0';
return str;
}
/* save channel specific user record */
static void botuser_save_chan(const char *key, USER_CHAN_REC *rec, CONFIG_NODE *node)
{
CONFIG_NODE *noderec;
char *str;
noderec = config_node_section(node, rec->channel, NODE_TYPE_BLOCK);
str = rec->flags == 0 ? NULL :
botuser_value2flags(rec->flags);
config_node_set_str(userconfig, noderec, "flags", str);
g_free_not_null(str);
}
static void botuser_config_save(USER_REC *user)
{
CONFIG_NODE *node, *subnode, *noderec;
GSList *tmp;
char *str;
node = config_node_traverse(userconfig, "users", TRUE);
node = config_node_section(node, user->nick, NODE_TYPE_BLOCK);
str = user->flags == 0 ? NULL :
botuser_value2flags(user->flags);
config_node_set_str(