mirror of
https://github.com/anope/anope.git
synced 2026-06-25 05:56:38 +02:00
9ff6ed2430
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@2313 5417fbe8-f217-4b02-8779-1006273d7864
725 lines
16 KiB
C
725 lines
16 KiB
C
/* Routines to maintain a list of connected servers
|
|
*
|
|
* (C) 2003-2009 Anope Team
|
|
* Contact us at team@anope.org
|
|
*
|
|
* Please read COPYING and README for further details.
|
|
*
|
|
* Based on the original code of Epona by Lara.
|
|
* Based on the original code of Services by Andy Church.
|
|
*
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "services.h"
|
|
#include "modules.h"
|
|
|
|
Server *servlist = NULL;
|
|
Server *me_server = NULL; /* This are we */
|
|
Server *serv_uplink = NULL; /* This is our uplink */
|
|
uint32 uplink_capab;
|
|
char *uplink;
|
|
char *TS6UPLINK;
|
|
char *TS6SID;
|
|
|
|
/* For first_server / next_server */
|
|
static Server *server_cur;
|
|
|
|
CapabInfo capab_info[] = {
|
|
{"NOQUIT", CAPAB_NOQUIT},
|
|
{"TSMODE", CAPAB_TSMODE},
|
|
{"UNCONNECT", CAPAB_UNCONNECT},
|
|
{"NICKIP", CAPAB_NICKIP},
|
|
{"SSJOIN", CAPAB_NSJOIN},
|
|
{"ZIP", CAPAB_ZIP},
|
|
{"BURST", CAPAB_BURST},
|
|
{"TS5", CAPAB_TS5},
|
|
{"TS3", CAPAB_TS3},
|
|
{"DKEY", CAPAB_DKEY},
|
|
{"PT4", CAPAB_PT4},
|
|
{"SCS", CAPAB_SCS},
|
|
{"QS", CAPAB_QS},
|
|
{"UID", CAPAB_UID},
|
|
{"KNOCK", CAPAB_KNOCK},
|
|
{"CLIENT", CAPAB_CLIENT},
|
|
{"IPV6", CAPAB_IPV6},
|
|
{"SSJ5", CAPAB_SSJ5},
|
|
{"SN2", CAPAB_SN2},
|
|
{"TOK1", CAPAB_TOKEN},
|
|
{"TOKEN", CAPAB_TOKEN},
|
|
{"VHOST", CAPAB_VHOST},
|
|
{"SSJ3", CAPAB_SSJ3},
|
|
{"SJB64", CAPAB_SJB64},
|
|
{"CHANMODES", CAPAB_CHANMODE},
|
|
{"NICKCHARS", CAPAB_NICKCHARS},
|
|
{NULL, 0}
|
|
};
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Return the first server in the server struct
|
|
* @param flags Server Flags, see services.h
|
|
* @return Server Struct
|
|
*/
|
|
Server *first_server(int flags)
|
|
{
|
|
server_cur = servlist;
|
|
if (flags > -1) {
|
|
while (server_cur && (server_cur->flags != flags))
|
|
server_cur = next_server(flags);
|
|
}
|
|
return server_cur;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Return the next server in the server struct
|
|
* @param flags Server Flags, see services.h
|
|
* @return Server Struct
|
|
*/
|
|
Server *next_server(int flags)
|
|
{
|
|
if (!server_cur)
|
|
return NULL;
|
|
|
|
do {
|
|
if (server_cur->links) {
|
|
server_cur = server_cur->links;
|
|
} else if (server_cur->next) {
|
|
server_cur = server_cur->next;
|
|
} else {
|
|
do {
|
|
server_cur = server_cur->uplink;
|
|
if (server_cur && server_cur->next) {
|
|
server_cur = server_cur->next;
|
|
break;
|
|
}
|
|
} while (server_cur);
|
|
}
|
|
} while (server_cur && ((flags > -1) && (server_cur->flags != flags)));
|
|
|
|
return server_cur;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* This function makes a new Server structure and links it in the right
|
|
* places in the linked list if a Server struct to it's uplink if provided.
|
|
* It can also be NULL to indicate it's the uplink and should be first in
|
|
* the server list.
|
|
* @param server_uplink Server struct
|
|
* @param name Server Name
|
|
* @param desc Server Description
|
|
* @param flags Server Flags, see services.h
|
|
* @param suid Server Universal ID
|
|
* @return Server Struct
|
|
*/
|
|
Server *new_server(Server * server_uplink, const char *name, const char *desc,
|
|
uint16 flags, const char *suid)
|
|
{
|
|
Server *serv;
|
|
|
|
if (debug)
|
|
alog("Creating %s(%s) uplinked to %s", name, suid, server_uplink ? server_uplink->name : "No uplink");
|
|
serv = new Server;
|
|
if (!name)
|
|
name = "";
|
|
serv->name = sstrdup(name);
|
|
serv->desc = sstrdup(desc);
|
|
serv->flags = flags;
|
|
serv->uplink = server_uplink;
|
|
if (suid) {
|
|
serv->suid = sstrdup(suid);
|
|
} else {
|
|
serv->suid = NULL;
|
|
}
|
|
if (ircd->sync)
|
|
serv->sync = SSYNC_IN_PROGRESS;
|
|
else
|
|
serv->sync = SSYNC_UNKNOWN;
|
|
serv->links = NULL;
|
|
serv->prev = NULL;
|
|
|
|
if (!server_uplink) {
|
|
serv->hops = 0;
|
|
serv->next = servlist;
|
|
if (servlist)
|
|
servlist->prev = serv;
|
|
servlist = serv;
|
|
} else {
|
|
serv->hops = server_uplink->hops + 1;
|
|
serv->next = server_uplink->links;
|
|
if (server_uplink->links)
|
|
server_uplink->links->prev = serv;
|
|
server_uplink->links = serv;
|
|
}
|
|
/* Check if this is our uplink server */
|
|
if ((server_uplink == me_server) && !(flags & SERVER_JUPED)) {
|
|
serv_uplink = serv;
|
|
serv->flags |= SERVER_ISUPLINK;
|
|
}
|
|
|
|
return serv;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Remove and free a Server structure. This function is the most complete
|
|
* remove treatment a server can get, as it first quits all clients which
|
|
* still pretend to be on this server, then it walks through all connected
|
|
* servers and disconnects them too. If all mess is cleared, the server
|
|
* itself will be too.
|
|
* @param Server struct
|
|
* @param reason the server quit
|
|
* @return void
|
|
*/
|
|
static void delete_server(Server * serv, const char *quitreason)
|
|
{
|
|
Server *s, *snext;
|
|
User *u, *unext;
|
|
NickAlias *na;
|
|
|
|
if (debug)
|
|
alog("Deleting %s(%s) uplinked to %s(%s)", serv->name, serv->suid, serv->uplink ? serv->uplink->name : "NOTHING", serv->uplink ? serv->uplink->suid : "NOSUIDUPLINK");
|
|
|
|
if (!serv) {
|
|
if (debug) {
|
|
alog("debug: delete_server() called with NULL arg!");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (debug)
|
|
alog("debug: delete_server() called for %s", serv->name);
|
|
|
|
if (ircdcap->noquit || ircdcap->qs) {
|
|
if ((uplink_capab & ircdcap->noquit)
|
|
|| (uplink_capab & ircdcap->qs)) {
|
|
u = firstuser();
|
|
while (u) {
|
|
unext = nextuser();
|
|
if (u->server == serv)
|
|
{
|
|
if ((na = findnick(u->nick)) && !(na->status & NS_FORBIDDEN)
|
|
&& (!(na->nc->flags & NI_SUSPENDED))
|
|
&& (na->status & (NS_IDENTIFIED | NS_RECOGNIZED))) {
|
|
na->last_seen = time(NULL);
|
|
if (na->last_quit)
|
|
delete [] na->last_quit;
|
|
na->last_quit =
|
|
(quitreason ? sstrdup(quitreason) : NULL);
|
|
}
|
|
|
|
if (LimitSessions) {
|
|
del_session(u->host);
|
|
}
|
|
delete u;
|
|
}
|
|
u = unext;
|
|
}
|
|
if (debug)
|
|
alog("debug: delete_server() cleared all users");
|
|
}
|
|
}
|
|
|
|
s = serv->links;
|
|
while (s) {
|
|
snext = s->next;
|
|
delete_server(s, quitreason);
|
|
s = snext;
|
|
}
|
|
|
|
if (debug)
|
|
alog("debug: delete_server() cleared all servers");
|
|
|
|
delete [] serv->name;
|
|
delete [] serv->desc;
|
|
if (serv->prev)
|
|
serv->prev->next = serv->next;
|
|
if (serv->next)
|
|
serv->next->prev = serv->prev;
|
|
if (serv->uplink->links == serv)
|
|
serv->uplink->links = serv->next;
|
|
|
|
if (debug)
|
|
alog("debug: delete_server() completed");
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Find a server by name, returns NULL if not found
|
|
* @param s Server struct
|
|
* @param name Server Name
|
|
* @return Server struct
|
|
*/
|
|
Server *findserver(Server * s, const char *name)
|
|
{
|
|
Server *sl;
|
|
|
|
if (!name || !*name) {
|
|
return NULL;
|
|
}
|
|
|
|
if (debug)
|
|
alog("findserver(%s)", name);
|
|
|
|
while (s && (stricmp(s->name, name) != 0))
|
|
{
|
|
if (debug >= 3)
|
|
alog("Compared %s, not a match", s->name);
|
|
if (s->links)
|
|
{
|
|
sl = findserver(s->links, name);
|
|
if (sl)
|
|
{
|
|
s = sl;
|
|
}
|
|
else
|
|
{
|
|
s = s->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s = s->next;
|
|
}
|
|
}
|
|
|
|
if (debug)
|
|
alog("debug: findserver(%s) -> %p", name, static_cast<void *>(s));
|
|
return s;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Find a server by UID, returns NULL if not found
|
|
* @param s Server struct
|
|
* @param name Server Name
|
|
* @return Server struct
|
|
*/
|
|
Server *findserver_uid(Server * s, const char *name)
|
|
{
|
|
Server *sl;
|
|
|
|
if (!name || !*name) {
|
|
return NULL;
|
|
}
|
|
|
|
if (debug)
|
|
alog("debug: findserver_uid(%s)", name);
|
|
|
|
while (s && s->suid && (stricmp(s->suid, name) != 0))
|
|
{
|
|
if (debug >= 3)
|
|
alog("Compared %s, not a match", s->suid);
|
|
if (s->links)
|
|
{
|
|
sl = findserver_uid(s->links, name);
|
|
if (sl)
|
|
{
|
|
s = sl;
|
|
}
|
|
else
|
|
{
|
|
s = s->next;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
s = s->next;
|
|
}
|
|
}
|
|
|
|
if (debug)
|
|
alog("debug: findserver_uid(%s) -> %p", name, static_cast<void *>(s));
|
|
return s;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Find if the server is synced with the network
|
|
* @param s Server struct
|
|
* @param name Server Name
|
|
* @return Not Synced returns -1, Synced returns 1, Error returns 0
|
|
*/
|
|
int anope_check_sync(const char *name)
|
|
{
|
|
Server *s;
|
|
s = findserver(servlist, name);
|
|
|
|
if (!s)
|
|
return 0;
|
|
|
|
if (is_sync(s))
|
|
return 1;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Handle adding the server to the Server struct
|
|
* @param source Name of the uplink if any
|
|
* @param servername Name of the server being linked
|
|
* @param hops Number of hops to reach this server
|
|
* @param descript Description of the server
|
|
* @param numeric Server Numberic/SUID
|
|
* @return void
|
|
*/
|
|
void do_server(const char *source, const char *servername, const char *hops,
|
|
const char *descript, const char *numeric)
|
|
{
|
|
Server *s;
|
|
|
|
if (debug) {
|
|
if (!*source) {
|
|
alog("debug: Server introduced (%s)", servername);
|
|
} else {
|
|
alog("debug: Server introduced (%s) from %s", servername,
|
|
source);
|
|
}
|
|
}
|
|
|
|
if (source[0] == '\0')
|
|
s = me_server;
|
|
else
|
|
s = findserver(servlist, source);
|
|
|
|
if (s == NULL)
|
|
s = findserver_uid(servlist, source);
|
|
|
|
if (s == NULL)
|
|
throw CoreException("Recieved a server from a nonexistant uplink?");
|
|
|
|
/* Create a server structure. */
|
|
new_server(s, servername, descript, 0, numeric);
|
|
|
|
/* Announce services being online. */
|
|
if (GlobalOnCycle && GlobalOnCycleUP)
|
|
notice_server(s_GlobalNoticer, s, "%s", GlobalOnCycleUP);
|
|
|
|
/* Let modules know about the connection */
|
|
FOREACH_MOD(I_OnServerConnect, OnServerConnect(s));
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Handle removing the server from the Server struct
|
|
* @param source Name of the server leaving
|
|
* @param ac Number of arguments in av
|
|
* @param av Agruments as part of the SQUIT
|
|
* @return void
|
|
*/
|
|
void do_squit(const char *source, int ac, const char **av)
|
|
{
|
|
char buf[BUFSIZE];
|
|
Server *s;
|
|
|
|
if (ircd->ts6) {
|
|
s = findserver_uid(servlist, av[0]);
|
|
if (!s) {
|
|
s = findserver(servlist, av[0]);
|
|
}
|
|
} else {
|
|
s = findserver(servlist, av[0]);
|
|
}
|
|
if (!s) {
|
|
alog("SQUIT for nonexistent server (%s)!!", av[0]);
|
|
return;
|
|
}
|
|
FOREACH_MOD(I_OnServerQuit, OnServerQuit(s));
|
|
|
|
/* If this is a juped server, send a nice global to inform the online
|
|
* opers that we received it.
|
|
*/
|
|
if (s->flags & SERVER_JUPED) {
|
|
snprintf(buf, BUFSIZE, "Received SQUIT for juped server %s",
|
|
s->name);
|
|
ircdproto->SendGlobops(s_OperServ, buf);
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "%s %s", s->name,
|
|
(s->uplink ? s->uplink->name : ""));
|
|
|
|
if (ircdcap->unconnect) {
|
|
if ((s->uplink == me_server)
|
|
&& (uplink_capab & ircdcap->unconnect)) {
|
|
if (debug) {
|
|
alog("debug: Sending UNCONNECT SQUIT for %s", s->name);
|
|
}
|
|
/* need to fix */
|
|
ircdproto->SendSquit(s->name, buf);
|
|
}
|
|
}
|
|
|
|
delete_server(s, buf);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Handle parsing the CAPAB/PROTOCTL messages
|
|
* @param ac Number of arguments in av
|
|
* @param av Agruments
|
|
* @return void
|
|
*/
|
|
void capab_parse(int ac, const char **av)
|
|
{
|
|
int i;
|
|
int j;
|
|
char *s, *tmp;
|
|
|
|
const char *temp;
|
|
|
|
for (i = 0; i < ac; i++) {
|
|
temp = av[i];
|
|
|
|
s = myStrGetToken(temp, '=', 0);
|
|
tmp = myStrGetTokenRemainder(temp, '=', 1);
|
|
|
|
if (!s)
|
|
continue;
|
|
|
|
for (j = 0; capab_info[j].token; j++) {
|
|
if (stricmp(s, capab_info[j].token) == 0)
|
|
uplink_capab |= capab_info[j].flag;
|
|
/* Special cases */
|
|
if ((stricmp(s, "NICKIP") == 0) && !ircd->nickip)
|
|
ircd->nickip = 1;
|
|
if ((stricmp(s, "CHANMODES") == 0) && tmp)
|
|
ircd->chanmodes = sstrdup(tmp);
|
|
if ((stricmp(s, "NICKCHARS") == 0) && tmp)
|
|
ircd->nickchars = sstrdup(tmp);
|
|
}
|
|
|
|
delete [] s;
|
|
if (tmp)
|
|
delete [] tmp;
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* Search the uline servers array to find out if the server that just set the
|
|
* mode is in our uline list
|
|
* @param server Server Setting the mode
|
|
* @return int 0 if not found, 1 if found
|
|
*/
|
|
int is_ulined(const char *server)
|
|
{
|
|
int j;
|
|
|
|
for (j = 0; j < NumUlines; j++) {
|
|
if (stricmp(Ulines[j], server) == 0) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* See if the current server is synced, or has an unknown sync state
|
|
* (in which case we pretend it is always synced)
|
|
* @param server Server of which we want to know the state
|
|
* @return int 0 if not synced, 1 if synced
|
|
*/
|
|
int is_sync(Server * server)
|
|
{
|
|
if ((server->sync == SSYNC_DONE) || (server->sync == SSYNC_UNKNOWN))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Finish the syncing process for this server and (optionally) for all
|
|
* it's leaves as well
|
|
* @param serv Server to finish syncing
|
|
* @param sync_links Should all leaves be synced as well? (1: yes, 0: no)
|
|
* @return void
|
|
*/
|
|
void finish_sync(Server * serv, int sync_links)
|
|
{
|
|
Server *s;
|
|
|
|
if (!serv || is_sync(serv))
|
|
return;
|
|
|
|
/* Mark each server as in sync */
|
|
s = serv;
|
|
do {
|
|
if (!is_sync(s)) {
|
|
if (debug)
|
|
alog("debug: Finishing sync for server %s", s->name);
|
|
|
|
s->sync = SSYNC_DONE;
|
|
}
|
|
|
|
if (!sync_links)
|
|
break;
|
|
|
|
if (s->links) {
|
|
s = s->links;
|
|
} else if (s->next) {
|
|
s = s->next;
|
|
} else {
|
|
do {
|
|
s = s->uplink;
|
|
if (s == serv)
|
|
s = NULL;
|
|
if (s == me_server)
|
|
s = NULL;
|
|
} while (s && !(s->next));
|
|
if (s)
|
|
s = s->next;
|
|
}
|
|
} while (s);
|
|
|
|
/* Do some general stuff which should only be done once */
|
|
restore_unsynced_topics();
|
|
alog("Server %s is done syncing", serv->name);
|
|
}
|
|
|
|
/*******************************************************************/
|
|
|
|
/* TS6 UID generator common code.
|
|
*
|
|
* Derived from atheme-services, uid.c (hg 2954:116d46894b4c).
|
|
* -nenolod
|
|
*/
|
|
static int ts6_uid_initted = 0;
|
|
static char ts6_new_uid[10]; /* allow for \0 */
|
|
static unsigned int ts6_uid_index = 9; /* last slot in uid buf */
|
|
|
|
void ts6_uid_init()
|
|
{
|
|
snprintf(ts6_new_uid, 10, "%sAAAAAA", TS6SID);
|
|
ts6_uid_initted = 1;
|
|
}
|
|
|
|
void ts6_uid_increment(unsigned int slot)
|
|
{
|
|
if (slot != strlen(TS6SID)) {
|
|
if (ts6_new_uid[slot] == 'Z')
|
|
ts6_new_uid[slot] = '0';
|
|
else if (ts6_new_uid[slot] == '9') {
|
|
ts6_new_uid[slot] = 'A';
|
|
ts6_uid_increment(slot - 1);
|
|
} else
|
|
ts6_new_uid[slot]++;
|
|
} else {
|
|
if (ts6_new_uid[slot] == 'Z')
|
|
for (slot = 3; slot < 9; slot++)
|
|
ts6_new_uid[slot] = 'A';
|
|
else
|
|
ts6_new_uid[slot]++;
|
|
}
|
|
}
|
|
|
|
const char *ts6_uid_retrieve()
|
|
{
|
|
if (ircd->ts6 == 0)
|
|
{
|
|
alog("TS6 not supported on this ircd");
|
|
return "";
|
|
}
|
|
|
|
if (ts6_uid_initted != 1)
|
|
{
|
|
throw CoreException("TS6 IRCd and ts6_uid_init() hasn't been called!");
|
|
}
|
|
|
|
ts6_uid_increment(ts6_uid_index - 1);
|
|
return ts6_new_uid;
|
|
}
|
|
|
|
/*******************************************************************/
|
|
|
|
/*
|
|
* TS6 generator code, provided by DukePyrolator
|
|
*/
|
|
|
|
static int ts6_sid_initted = 0;
|
|
static char ts6_new_sid[4];
|
|
|
|
static void ts6_sid_increment(unsigned pos)
|
|
{
|
|
/*
|
|
* An SID must be exactly 3 characters long, starts with a digit,
|
|
* and the other two characters are A-Z or digits
|
|
* The rules for generating an SID go like this...
|
|
* --> ABCDEFGHIJKLMNOPQRSTUVWXYZ --> 0123456789 --> WRAP
|
|
*/
|
|
if (!pos)
|
|
{
|
|
/* At pos 0, if we hit '9', we've run out of available SIDs,
|
|
* reset the SID to the smallest possible value and try again. */
|
|
if (ts6_new_sid[pos] == '9')
|
|
{
|
|
ts6_new_sid[0] = '0';
|
|
ts6_new_sid[1] = 'A';
|
|
ts6_new_sid[2] = 'A';
|
|
}
|
|
else
|
|
// But if we haven't, just keep incrementing merrily.
|
|
++ts6_new_sid[0];
|
|
}
|
|
else
|
|
{
|
|
if (ts6_new_sid[pos] == 'Z')
|
|
ts6_new_sid[pos] = '0';
|
|
else if (ts6_new_sid[pos] == '9')
|
|
{
|
|
ts6_new_sid[pos] = 'A';
|
|
ts6_sid_increment(pos - 1);
|
|
}
|
|
else
|
|
++ts6_new_sid[pos];
|
|
}
|
|
}
|
|
|
|
const char *ts6_sid_retrieve()
|
|
{
|
|
if (!ircd->ts6)
|
|
{
|
|
if (debug)
|
|
alog("TS6 not supported on this ircd");
|
|
return "";
|
|
}
|
|
|
|
if (!ts6_sid_initted)
|
|
{
|
|
// Initialize ts6_new_sid with the services server SID
|
|
snprintf(ts6_new_sid, 4, "%s", TS6SID);
|
|
ts6_sid_initted = 1;
|
|
}
|
|
while (1)
|
|
{
|
|
// Check if the new SID is used by a known server
|
|
if (!findserver_uid(servlist, ts6_new_sid))
|
|
// return the new SID
|
|
return ts6_new_sid;
|
|
|
|
// Add one to the last SID
|
|
ts6_sid_increment(2);
|
|
}
|
|
/* not reached */
|
|
return "";
|
|
}
|
|
|
|
/* EOF */
|