FLAG_DEBUG, /**< send global debug/anti-hack info */
FLAG_ACCOUNT, /**< account name has been set */
FLAG_HIDDENHOST, /**< user's host is hidden */
+ FLAG_NOTCONN, /**< nobody is connected to this client */
FLAG_FAKEHOST, /**< user has a fakehost */
FLAG_FAKEIDENT, /**< user has a fakeident */
FLAG_NOCHAN, /**< hide user's channels for non-opers */
#define IsAccount(x) HasFlag(x, FLAG_ACCOUNT)
/** Return non-zero if the client has set mode +x (hidden host). */
#define IsHiddenHost(x) HasFlag(x, FLAG_HIDDENHOST)
+/** Return non-zero if nobody is connected to this client structure */
+#define IsNotConn(x) HasFlag(x, FLAG_NOTCONN)
/** Return non-zero if the client has an active PING request. */
#define IsPingSent(x) HasFlag(x, FLAG_PINGSENT)
/** Return non-zero if the client's channels are hidden. */
#define SetAccount(x) SetFlag(x, FLAG_ACCOUNT)
/** Mark a client as having mode +x (hidden host). */
#define SetHiddenHost(x) SetFlag(x, FLAG_HIDDENHOST)
+/** Mark a client as not having anyone connected to it */
+#define SetNotConn(x) SetFlag(x, FLAG_NOTCONN)
/** Mark a client as having a fakehost. */
#define SetFakeHost(x) SetFlag(x, FLAG_FAKEHOST)
#define SetFakeIdent(x) SetFlag(x, FLAG_FAKEIDENT)
#define ClearServNotice(x) ClrFlag(x, FLAG_SERVNOTICE)
/** Remove mode +x (hidden host) from the client. */
#define ClearHiddenHost(x) ClrFlag(x, FLAG_HIDDENHOST)
+/** Mark client as having someone connected to it */
+#define ClearNotConn(x) ClrFlag(x, FLAG_NOTCONN)
/** Remove fakehost flag from the flient. */
#define ClearFakeHost(x) ClrFlag(x, FLAG_FAKEHOST)
#define ClearFakeIdent(x) ClrFlag(x, FLAG_FAKEIDENT)
extern int ms_svspart(struct Client*, struct Client*, int, char*[]);
extern int ms_topic(struct Client*, struct Client*, int, char*[]);
extern int ms_trace(struct Client*, struct Client*, int, char*[]);
+extern int ms_unzombie(struct Client*, struct Client*, int, char*[]);
extern int ms_uping(struct Client*, struct Client*, int, char*[]);
extern int ms_version(struct Client*, struct Client*, int, char*[]);
extern int ms_wallchops(struct Client*, struct Client*, int, char*[]);
extern int ms_wallusers(struct Client*, struct Client*, int, char*[]);
extern int ms_wallvoices(struct Client*, struct Client*, int, char*[]);
extern int ms_whois(struct Client*, struct Client*, int, char*[]);
+extern int ms_zombie(struct Client*, struct Client*, int, char*[]);
#endif /* INCLUDED_handlers_h */
#define TOK_ACCOUNT "AC"
#define CMD_ACCOUNT MSG_ACCOUNT, TOK_ACCOUNT
+#define MSG_ZOMBIE "ZOMBIE" /* ZOMB */
+#define TOK_ZOMBIE "ZO"
+#define CMD_ZOMBIE MSG_ZOMBIE, TOK_ZOMBIE
+
+#define MSG_UNZOMBIE "UNZOMBIE" /* UZMB */
+#define TOK_UNZOMBIE "ZU"
+#define CMD_UNZOMBIE MSG_UNZOMBIE, TOK_UNZOMBIE
+
#define MSG_ASLL "ASLL" /* ASLL */
#define TOK_ASLL "LL"
#define CMD_ASLL MSG_ASLL, TOK_ASLL
extern int connect_server(struct ConfItem* aconf, struct Client* by);
extern int net_close_unregistered_connections(struct Client* source);
extern void close_connection(struct Client *cptr);
+extern void connection_switch_to_client(struct Client *source, struct Client *target);
extern void add_connection(struct Listener* listener, int fd, ssl_session_t *ssl);
extern int read_message(time_t delay);
extern void init_server_identity(void);
extern int check_registered(struct Client *sptr);
extern int check_registered_user(struct Client *sptr);
+extern void zombie_client(struct Client *cptr, struct Client *killer,
+ struct Client *victim);
+extern void unzombie_client(struct Client *cptr, struct Client *sptr,
+ struct Client *acptr, struct Client *victim);
extern int exit_client(struct Client *cptr, struct Client *bcptr,
struct Client *sptr, const char *comment);
extern char *myctime(time_t value);
const char* nick, int parc, char* parv[], unsigned int force);
extern void send_umode_out(struct Client* cptr, struct Client* sptr,
struct Flags* old, int prop);
+extern void send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old,
+ int sendset);
extern int whisper(struct Client* source, const char* nick,
const char* channel, const char* text, int is_notice);
extern void send_user_info(struct Client* to, char* names, int rpl,
m_time.c \
m_topic.c \
m_trace.c \
+ m_unzombie.c \
m_uping.c \
m_user.c \
m_userhost.c \
m_who.c \
m_whois.c \
m_whowas.c \
+ m_zombie.c \
match.c \
memdebug.c \
motd.c \
if (!cptr)
continue;
-
+
+ /* We don't need to check zombies here */
+ if (IsNotConn(cptr)) {
+ assert(IsUser(cptr));
+ /* for now: reap after fixed time (15 minutes) */
+ if ((CurrentTime - cli_user(cptr)->last) >= 900) {
+ SetFlag(cptr, FLAG_DEADSOCKET);
+ /* this will be used as exit message */
+ ircd_strncpy(cli_info(cptr), "Ping timeout", REALLEN);
+ } else
+ continue;
+ }
+
assert(&me != cptr); /* I should never be in the local client array! */
sendto_opmask_butone(0, SNO_OLDSNO,
"No response from %s, closing link",
cli_name(cptr));
+ /*
+ * Keep client structure around when a user pings out, so that they can
+ * reconnect to it later
+ */
+ if (IsUser(cptr) && IsAccount(cptr)) {
+ zombie_client(&me, &me, cptr);
+ continue;
+ }
exit_client_msg(cptr, cptr, &me, "Ping timeout");
continue;
}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/m_unzombie.c
+ * Copyright (C) 2011 Jan Krueger <jk@jk.gs>
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "ircd.h"
+#include "ircd_log.h"
+#include "ircd_reply.h"
+#include "ircd_string.h"
+#include "msg.h"
+#include "numnicks.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "s_user.h"
+#include "send.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/** Handle an UNZOMBIE message from a server connection.
+ *
+ * \a parv has the following elements:
+ * \li \a parv[1] is the numnick of the client attaching to the zombie
+ * \li \a parv[2] is the numnick of the zombie
+ *
+ * See @ref m_functions for discussion of the arguments.
+ * @param[in] cptr Client that sent us the message.
+ * @param[in] sptr Original source of message.
+ * @param[in] parc Number of arguments.
+ * @param[in] parv Argument vector.
+ */
+int ms_unzombie(struct Client* cptr, struct Client* sptr, int parc,
+ char* parv[])
+{
+ struct Client *acptr;
+ struct Client *victim;
+
+ if (parc < 3)
+ return need_more_params(sptr, "UNZOMBIE");
+
+ if (!IsServer(sptr))
+ return protocol_violation(cptr, "UNZOMBIE from non-server %s",
+ cli_name(sptr));
+
+ if (!(acptr = findNUser(parv[1])))
+ return 0; /* If this is colliding with a QUIT, let the QUIT win */
+
+ if (!(victim = findNUser(parv[2])))
+ /* TODO send error */
+ ;
+
+ if (!IsNotConn(victim))
+ return protocol_violation(cptr, "UNZOMBIE trying to attach to non-zombie %s",
+ cli_name(victim));
+ assert(IsAccount(victim));
+
+ unzombie_client(cptr, sptr, acptr, victim);
+ return 0;
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/m_zombie.c
+ * Copyright (C) 2011 Jan Krueger <jk@jk.gs>
+ *
+ * See file AUTHORS in IRC package for additional names of
+ * the programmers.
+ *
+ * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#include "config.h"
+
+#include "client.h"
+#include "ircd.h"
+#include "ircd_log.h"
+#include "ircd_reply.h"
+#include "ircd_string.h"
+#include "msg.h"
+#include "numnicks.h"
+#include "s_debug.h"
+#include "s_misc.h"
+#include "s_user.h"
+#include "send.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/** Handle a ZOMBIE message from a server connection.
+ *
+ * \a parv has the following elements:
+ * \li \a parv[1] is the numnick of the client to act on
+ *
+ * See @ref m_functions for discussion of the arguments.
+ * @param[in] cptr Client that sent us the message.
+ * @param[in] sptr Original source of message.
+ * @param[in] parc Number of arguments.
+ * @param[in] parv Argument vector.
+ */
+int ms_zombie(struct Client* cptr, struct Client* sptr, int parc,
+ char* parv[])
+{
+ struct Client *acptr;
+
+ if (parc < 2)
+ return need_more_params(sptr, "ZOMBIE");
+
+ if (!IsServer(sptr))
+ return protocol_violation(cptr, "ZOMBIE from non-server %s",
+ cli_name(sptr));
+
+ if (!(acptr = findNUser(parv[1])))
+ return 0; /* Ignore for a user that QUIT; probably crossed (however unlikely) */
+
+ if (!IsAccount(acptr))
+ return protocol_violation(cptr, "ZOMBIE for user without account (%s)",
+ cli_name(acptr));
+
+ zombie_client(cptr, sptr, acptr);
+ return 0;
+}
/* UNREG, CLIENT, SERVER, OPER, SERVICE */
{ m_ignore, m_ignore, ms_account, m_ignore, m_ignore }
},
+ {
+ MSG_ZOMBIE,
+ TOK_ZOMBIE,
+ 0, MAXPARA, MFLG_SLOW, 0, NULL,
+ /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+ { m_ignore, m_ignore, ms_zombie, m_ignore, m_ignore }
+ },
+ {
+ MSG_UNZOMBIE,
+ TOK_UNZOMBIE,
+ 0, MAXPARA, MFLG_SLOW, 0, NULL,
+ /* UNREG, CLIENT, SERVER, OPER, SERVICE */
+ { m_ignore, m_ignore, ms_unzombie, m_ignore, m_ignore }
+ },
{
MSG_ASLL,
TOK_ASLL,
}
}
+/**
+ * Switches a client's connection over to a different, zombied client.
+ *
+ * @param source client to attach to zombied client
+ * @param target zombied client
+ */
+void connection_switch_to_client(struct Client *source, struct Client *target)
+{
+ assert(IsNotConn(target));
+ assert(MyConnect(source));
+ assert(-1 < cli_fd(source));
+ cli_connect(target) = cli_connect(source);
+ /* conveniently, this makes it much easier to get rid of source later on */
+ cli_from(target) = target;
+ LocalClientArray[cli_fd(source)] = target;
+
+ /* make main loop remove source soonish */
+ SetFlag(source, FLAG_DEADSOCKET);
+
+ /* need to copy over data from old client */
+ cli_user(target)->server = cli_user(source)->server;
+ strcpy(cli_yxx(target), cli_yxx(source));
+ cli_hopcount(target) = cli_hopcount(source);
+ cli_ip(target) = cli_ip(source);
+ strcpy(cli_username(target), cli_username(source));
+ strcpy(cli_user(target)->realhost, cli_user(source)->realhost);
+}
+
/** Close all unregistered connections.
* @param source Oper who requested the close.
* @return Number of closed connections.
}
}
+/**
+ * Marks a local client as disconnected, and close its link if it is a local client.
+ *
+ * @param cptr server that notified us
+ * @param killer origin of decision to zombie \a victim
+ * @param victim zombied client
+ */
+void zombie_client(struct Client *cptr, struct Client *killer, struct Client *victim)
+{
+ assert(IsServer(cptr) || IsMe(cptr));
+ assert(IsServer(killer) || IsMe(killer));
+ assert(IsUser(victim));
+
+ /*
+ * Stop a running /LIST clean
+ */
+ if (MyUser(victim) && cli_listing(victim)) {
+ MyFree(cli_listing(victim));
+ cli_listing(victim) = NULL;
+ }
+
+ if (MyConnect(victim))
+ close_connection(victim);
+ /* need this so that main loop doesn't exit the client */
+ ClrFlag(victim, FLAG_DEADSOCKET);
+
+ SetNotConn(victim);
+ sendcmdto_serv_butone(killer, CMD_ZOMBIE, cptr, "%C", victim);
+}
+
+/**
+ * Attaches a client to a zombied client, removing the superfluous client in the process.
+ *
+ * @param cptr server that notified us
+ * @param sptr origin server of unzombie operation
+ * @param acptr client that is attaching to \a victim
+ * @param victim zombied client that someone is attaching to
+ */
+void unzombie_client(struct Client *cptr, struct Client *sptr, struct Client *acptr, struct Client *victim)
+{
+ assert(IsServer(cptr) || IsMe(cptr));
+ assert(IsServer(sptr) || IsMe(sptr));
+ assert(IsUser(acptr));
+ assert(IsNotConn(victim));
+
+ if (MyConnect(acptr))
+ connection_switch_to_client(acptr, victim);
+
+ ClearOper(victim);
+ ClearNotConn(victim);
+
+ if (MyConnect(victim)) {
+ /* inform client about "new" modes */
+ struct Flags setflags = cli_flags(acptr);
+ struct Membership *chan;
+ sendcmdto_one(acptr, CMD_NICK, victim, "%C", victim);
+ send_umode(victim, victim, &setflags, ALL_UMODES);
+
+ /*
+ * mark current client as zombie on all channels so that it does not show
+ * up in the memberships we'll resend below
+ */
+ for (chan = cli_user(acptr)->channel; chan; chan = chan->next_channel) {
+ SetZombie(chan);
+ }
+
+ /* resend channel memberships */
+ for (chan = cli_user(victim)->channel; chan; chan = chan->next_channel) {
+ struct Channel *chptr = chan->channel;
+ /* pretty unlikely to happen but let's handle this anyway */
+ if (IsZombie(chan))
+ continue;
+ sendcmdto_one(victim, CMD_JOIN, victim, ":%H", chptr);
+ if (chptr->topic[0]) {
+ send_reply(victim, RPL_TOPIC, chptr->chname, chptr->topic);
+ send_reply(victim, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick,
+ chptr->topic_time);
+ }
+ do_names(victim, chptr, NAMES_ALL|NAMES_EON); /* send /names list */
+ }
+ }
+
+ sendcmdto_serv_butone(sptr, CMD_UNZOMBIE, cptr, "%C %C", acptr, victim);
+}
+
/* exit_client, rewritten 25-9-94 by Run */
/**
* Exits a client of *any* type (user, server, etc)
{ FLAG_WEBIRC, 'W' },
{ FLAG_SEE_IDLETIME,'t' },
{ FLAG_SECURITY_SERV,'D' },
- { FLAG_HIDDENHOST, 'x' }
+ { FLAG_HIDDENHOST, 'x' },
+ { FLAG_NOTCONN, 'Z' }
};
/** Length of #userModeList. */
case 'z': /* Formerly SSL mode; we ignore it. */
break;
#endif
+ case 'Z':
+ if (what == MODE_ADD)
+ SetNotConn(sptr);
+ else
+ ClearNotConn(sptr);
+ break;
default:
send_reply(sptr, ERR_UMODEUNKNOWNFLAG, *m);
break;
ClrFlag(sptr, FLAG_FAKEHOST);
if (!FlagHas(&setflags, FLAG_SEE_IDLETIME) && IsSeeIdletime(sptr))
ClrFlag(sptr, FLAG_SEE_IDLETIME);
+ if (!FlagHas(&setflags, FLAG_NOTCONN) && IsNotConn(sptr))
+ ClrFlag(sptr, FLAG_NOTCONN);
/*
* new umode; servers and privileged opers can set it, local users cannot;
* prevents users from /kick'ing or /mode -o'ing
static int can_send(struct Client* to)
{
assert(0 != to);
- return (IsDead(to) || IsMe(to) || -1 == cli_fd(to)) ? 0 : 1;
+ return (IsDead(to) || IsMe(to) || IsNotConn(to) || -1 == cli_fd(to)) ? 0 : 1;
}
/** Close the connection with the highest sendq.