1
0
mirror of https://github.com/anope/anope.git synced 2026-06-26 20:16:38 +02:00
Files
anope/src/servers.c
T
geniusdex geniusdex@31f1291d-b8d6-0310-a050-a5561fc1590b b5bf30483b BUILD : 1.7.21 (1351) BUGS : 831 NOTES : Fixed a bug in next_server() which skipped over all servers right away when passing -1 as param
git-svn-id: svn://svn.anope.org/anope/trunk@1351 31f1291d-b8d6-0310-a050-a5561fc1590b


git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@1066 5417fbe8-f217-4b02-8779-1006273d7864
2008-01-16 09:02:35 +00:00

619 lines
16 KiB
C

/* Routines to maintain a list of connected servers
*
* (C) 2003-2008 Anope Team
* Contact us at info@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"
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 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 * uplink, const char *name, const char *desc,
uint16 flags, char *suid)
{
Server *serv;
serv = scalloc(sizeof(Server), 1);
if (!name)
name = "";
serv->name = sstrdup(name);
serv->desc = sstrdup(desc);
serv->flags = flags;
serv->uplink = 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 (!uplink) {
serv->hops = 0;
serv->next = servlist;
if (servlist)
servlist->prev = serv;
servlist = serv;
} else {
serv->hops = uplink->hops + 1;
serv->next = uplink->links;
if (uplink->links)
uplink->links->prev = serv;
uplink->links = serv;
}
/* Check if this is our uplink server */
if ((uplink == me_server) && !(flags & SERVER_JUPED))
serv_uplink = serv;
/* Write the StartGlobal (to non-juped servers) */
if (GlobalOnCycle && GlobalOnCycleUP && !(flags & SERVER_JUPED))
notice_server(s_GlobalNoticer, serv, "%s", GlobalOnCycleUP);
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 (!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 = u->na) && !(na->status & NS_VERBOTEN)
&& (!(na->nc->flags & NI_SUSPENDED))
&& (na->status & (NS_IDENTIFIED | NS_RECOGNIZED))) {
na->last_seen = time(NULL);
if (na->last_quit)
free(na->last_quit);
na->last_quit =
(quitreason ? sstrdup(quitreason) : NULL);
}
if (LimitSessions) {
del_session(u->host);
}
delete_user(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");
free(serv->name);
free(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 >= 3) {
alog("debug: findserver(%p)", name);
}
while (s && (stricmp(s->name, name) != 0)) {
if (s->links) {
sl = findserver(s->links, name);
if (sl) {
s = sl;
} else {
s = s->next;
}
} else {
s = s->next;
}
}
if (debug >= 3) {
alog("debug: findserver(%s) -> %p", name, (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 >= 3) {
alog("debug: findserver_uid(%p)", name);
}
while (s && s->suid && (stricmp(s->suid, name) != 0)) {
if (s->links) {
sl = findserver_uid(s->links, name);
if (sl) {
s = sl;
} else {
s = s->next;
}
} else {
s = s->next;
}
}
if (debug >= 3) {
alog("debug: findserver_uid(%s) -> %p", name, (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, char *servername, char *hops,
char *descript, 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);
new_server(s, servername, descript, 0, numeric);
send_event(EVENT_SERVER_CONNECT, 1, servername);
}
/*************************************************************************/
/**
* 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, char **av)
{
char buf[BUFSIZE];
Server *s;
if (UseTS6 && 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;
}
send_event(EVENT_SERVER_SQUIT, 1, s->name);
/* 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);
anope_cmd_global(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 */
anope_cmd_squit(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, char **av)
{
int i;
int j;
char *s, *tmp;
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);
}
if (s)
free(s);
if (tmp)
free(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(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(void)
{
unsigned int i;
char buf[BUFSIZE];
/* check just in case... you can never be too safe. */
if (TS6SID != NULL) {
snprintf(ts6_new_uid, 10, "%sAAAAAA", TS6SID);
ts6_uid_initted = 1;
} else {
alog("warning: no TS6SID specified, disabling TS6 support.");
UseTS6 = 0;
return;
}
}
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]++;
}
}
char *ts6_uid_retrieve(void)
{
if (ts6_uid_initted != 1)
ts6_uid_init();
ts6_uid_increment(ts6_uid_index - 1);
return ts6_new_uid;
}
/* EOF */