/************************************************************************
* IRC - Internet Relay Chat, modules/m_server.c
*
* Copyright (C) 2000-2003 TR-IRCD Development
*
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* 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 2, 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: m_server.c,v 1.5 2003/06/14 13:55:51 tr-ircd Exp $
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "s_conf.h"
#include "confitem.h"
#include "hook.h"
#include "h.h"
static char *token = TOK1_SERVER;
static int hookid_inform_remote_servers = 0;
static struct Message _msgtab[] = {
{MSG_SERVER, 0, MAXPARA, M_SLOW, 0L,
m_server, m_ignore, m_ignore, s_server, m_ignore}
};
#ifndef STATIC_MODULES
char *_version = "$Revision: 1.5 $";
void _modinit(void)
{
mod_add_cmd(_msgtab);
tok1_msgtab[(u_char) *token].msg = _msgtab;
hookid_inform_remote_servers = hook_add_event("inform remote servers");
}
void _moddeinit(void)
{
mod_del_cmd(_msgtab);
tok1_msgtab[(u_char) *token].msg = NULL;
}
#else
void m_server_init(void)
{
mod_add_cmd(_msgtab);
tok1_msgtab[(u_char) *token].msg = _msgtab;
hookid_inform_remote_servers = hook_add_event("inform remote servers");
}
#endif
/*
* bogus_host
*
* inputs - hostname
* output - 1 if a bogus hostname input, 0 if its valid
* side effects - none
*/
static int bogus_host(char *host)
{
int bogus_server = 0;
char *s;
int dots = 0;
for (s = host; *s; s++) {
if (!IsServChar(*s)) {
bogus_server = 1;
break;
}
if ('.' == *s)
++dots;
}
if (!dots || bogus_server)
return 1;
return 0;
}
/*
* parse_server_args
*
* inputs - parv parameters
* - parc count
* - info string (to be filled in by this routine)
* - hop count (to be filled in by this routine)
* output - NULL if invalid params, server name otherwise
* side effects - parv[1] is trimmed to HOSTLEN size if needed.
*/
static char *parse_server_args(char *parv[], int parc, char *info,
char *sid, int *flags, int *hop)
{
char *name;
info[0] = '\0';
sid[0] = '\0';
if (parc < 2 || *parv[1] == '\0')
return NULL;
*hop = 0;
*flags = 0;
name = parv[1];
if (parc == 6) {
*hop = atoi(parv[2]);
*flags |= strchr(parv[3], 'H') ? PFLAGS_DOHIDENAME : 0;
*flags |= strchr(parv[3], 'U') ? PFLAGS_ULINE : 0;
*flags |= strchr(parv[3], 'R') ? PFLAGS_ISHUB : 0;
strlcpy_irc(sid, parv[4] + 1, 8);
sid[8] = '\0';
strlcpy_irc(info, parv[5], REALLEN);
info[REALLEN] = '\0';
} else if ((parc == 5) && (parv[3][0] != '!')) {
*hop = atoi(parv[2]);
*flags |= strchr(parv[3], 'H') ? PFLAGS_DOHIDENAME : 0;
*flags |= strchr(parv[3], 'U') ? PFLAGS_ULINE : 0;
*flags |= strchr(parv[3], 'R') ? PFLAGS_ISHUB : 0;
strlcpy_irc(info, parv[4], REALLEN);
info[REALLEN] = '\0';
} else if ((parc == 5) && (parv[3][0] == '!')) { /* Kludge for Halycon */
*hop = atoi(parv[2]);
strlcpy_irc(sid, parv[3] + 1, 8);
sid[8] = '\0';
strlcpy_irc(info, parv[4], REALLEN);
info[REALLEN] = '\0';
} else if (parc == 4) {
*hop = atoi(parv[2]);
*flags = 0;
strlcpy_irc(info, parv[3], REALLEN);
info[REALLEN] = '\0';
} else if (parc == 3) {
*hop = 1;
*flags = 0;
strlcpy_irc(info, parv[2], REALLEN);
info[REALLEN] = '\0';
} else if (parc == 2) {
*hop = 1;
*flags = 0;
strlcpy_irc(info, "no description", REALLEN);
info[REALLEN] = '\0';
}
if (strlen(name) > HOSTLEN)
name[HOSTLEN] = '\0';
return (name);
}
/*
* server_exists()
*
* inputs - servername
* output - 1 if server exists, 0 if doesnt exist
*/
static struct Client *server_exists(char *servername)
{
struct Client *target_p;
dlink_node *ptr;
for (ptr = global_serv_list.head; ptr; ptr = ptr->next) {
target_p = ptr->data;
if (!match(target_p->name, servername) || !match(servername, target_p->name))
return target_p;
}
return NULL;
}
/*
* * m_server
* parv[0] = sender prefix
* parv[1] = servername
* parv[2] = serverinfo
*/
int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
char info[REALLEN + 1];
char *host;
char sid[8];
struct Client *target_p;
int hop;
int flags = 0;
if ((host = parse_server_args(parv, parc, info, sid, &flags, &hop)) == NULL) {
sendto_one_server(cptr, NULL, TOK1_ERROR, ":No servername");
return 0;
}
if (!DoesTS(cptr)) {
sendto_gnotice("Link %s dropped, non-TS server", get_client_name(cptr, MASK_IP));
return exit_client(cptr, cptr, "Non-TS server");
}
if (bogus_host(host)) {
return exit_client(cptr, cptr, "Bogus server name");
}
/* Now we just have to call check_server and everything should be
* check for us... -A1kmm. */
switch (check_server(host, cptr)) {
case -2:
sendto_gnotice("Unauthorized server connection attempt from %s: No entry for "
"servername %s", get_client_name(cptr, HIDE_IP), host);
return exit_client(cptr, cptr, "Invalid servername.");
break;
case NOT_AUTHORIZED:
sendto_gnotice("Unauthorized server connection attempt from %s: Bad password "
"for server %s", get_client_name(cptr, HIDE_IP), host);
return exit_client(cptr, cptr, "Invalid password.");
break;
case -3:
sendto_gnotice("Unauthorized server connection attempt from %s: Invalid host "
"for server %s", get_client_name(cptr, HIDE_IP), host);
return exit_client(cptr, cptr, "Invalid host.");
break;
case INVALID_CONNECTION:
sendto_gnotice("Unauthorized server connection attempt from %s to non-server port",
get_client_name(cptr, HIDE_IP));
return exit_client(cptr, cptr, "No server port");
break;
}
if ((target_p = server_exists(host))) {
/*
* This link is trying feed me a server that I already have
* access through another path -- multiple paths not accepted
* currently, kill this link immediately!!
*
* Rather than KILL the link which introduced it, KILL the
* youngest of the two links. -avalon
*
* Definitely don't do that here. This is from an unregistered
* connect - A1kmm.
*/
sendto_gnotice("Attempt to re-introduce server %s from %s", host,
get_client_name(cptr, HIDE_IP));
sendto_one_server(cptr, NULL, TOK1_ERROR, ":Server already exists.");
return exit_client(cptr, cptr, "Server Exists");
}
/*
* if we are connecting (Handshake), we already have the name from the
* C:line in client_p->name
*/
strlcpy_irc(cptr->name, host, HOSTLEN);
strlcpy_irc(cptr->info, info, REALLEN);
cptr->hopcount = hop;
cptr->protoflags |= flags;
return server_estab(cptr);
}
/*
* * m_server
* parv[0] = sender prefix
* parv[1] = servername
* parv[2] = hopcount
* parv[3] = hidechar
* parv[4] = serverid
* parv[5] = info
*/
int s_server(aClient *client_p, aClient *source_p, int parc, char *parv[])
{
char info[REALLEN + 1];
char *name;
struct Client *target_p;
struct hook_data thisdata;
int hop; /* for hopcount information */
char sid[8]; /* for server id */
int flags = 0; /* for hidechar information */
if ((name = parse_server_args(parv, parc, info, sid, &flags, &hop)) == NULL) {
sendto_one_server(client_p, NULL, TOK1_ERROR, ":No servername");
return 0;
}
if ((target_p = server_exists(name))) {
/*
* This link is trying feed me a server that I already have
* access through another path -- multiple paths not accepted
* currently, kill this link immediately!!
*
* Rather than KILL the link which introduced it, KILL the
* youngest of the two links. -avalon
*
* I think that we should exit the link itself, not the introducer,
* and we should always exit the most recently received(i.e. the
* one we are receiving this SERVER for. -A1kmm
*
* You *cant* do this, if you link somewhere, it bursts you a server
* that already exists, then sends you a client burst, you squit the
* server, but you keep getting the burst of clients on a server that
* doesnt exist, although ircd can handle it, its not a realistic
* solution.. --fl_
*/
/* It is behind a host-masked server. Completely ignore the
* server message(don't propagate or we will delink from whoever
* we propagate to). -A1kmm */
if (irc_strcmp(target_p->name, name) && target_p->from == client_p)
return 0;
if (client_p->firsttime > target_p->from->firsttime) {
sendto_one_server(client_p, NULL, TOK1_ERROR, ":Server %s already exists", name);
sendto_gnotice("Link %s cancelled, server %s already exists",
get_client_name(client_p, SHOW_IP), name);
return exit_client(client_p, &me, "Server Exists");
} else {
sendto_one_server(target_p->from, NULL, TOK1_ERROR, ":Server %s already exists", name);
sendto_gnotice("Link %s cancelled, server %s reintroduced by %s",
get_client_name(target_p->from, SHOW_IP), name, client_p->name);
return exit_client(target_p->from, &me, "Server Exists");
}
}
if (sid[0] && IsIDCapable(client_p)) {
if (!valid_base64_server_id(sid)) {
sendto_one_server(client_p, NULL, TOK1_ERROR, ":Invalid identity %s", sid);
return 0;
}
}
if (sid[0] && (target_p = find_server_by_base64_id(sid, NULL))) {
/*
* we have duplicate identities on the network.
* this is most likely the result of some misconfiguration.
* throw it out.
*/
sendto_one_server(client_p, NULL, TOK1_ERROR, ":Duplicate identity!");
sendto_gnotice("Link %s cancelled, identity %s already held by %s",
get_client_name(client_p, HIDEME), sid, target_p->name);
return exit_client(client_p, &me, "Duplicate identity");
}
/*
* User nicks never have '.' in them and server names
* must always have '.' in them.
*/
if (strchr(name, '.') == NULL) {
/*
* Server trying to use the same name as a person. Would
* cause a fair bit of confusion. Enough to make it hellish
* for a while and servers to send stuff to the wrong place.
*/
sendto_one_server(client_p, NULL, TOK1_ERROR, ":Nickname %s already exists!", name);
sendto_gnotice("Link %s cancelled: Server/nick collision on %s",
/* inpath */ get_client_name(client_p, HIDE_IP),
name);
return exit_client(client_p, client_p, "Nick as Server");
}
/*
* Server is informing about a new server behind
* this link. Create REMOTE server structure,
* add it to list and propagate word to my other
* server links...
*/
if (parc == 1 || info[0] == '\0') {
sendto_one_server(client_p, NULL, TOK1_ERROR, ":No server info specified for %s", name);
return 0;
}
/*
* See if the newly found server is behind a guaranteed
* leaf. If so, close the link.
* Ok, check client_p can hub the new server, and make sure it's not a LL
*/
if (!IsHub(client_p)) {
sendto_gnotice("Non-Hub link %s introduced %s.", get_client_name(client_p, HIDE_IP), name);
/* If it is new, we are probably misconfigured, so split the
* non-hub server introducing this. Otherwise, split the new
* server. -A1kmm. */
if ((timeofday - source_p->firsttime) < 20) {
return exit_client(source_p, &me, "No H-line.");
} else {
sendto_one_server(source_p, &me, TOK1_SQUIT, "%s :Sorry, no H-line.", name);
return 0;
}
}
/*
* Since it is possible, that the remote server uses old protocol,
* we might not know if they are U:Lined since the SERVER line
* contains less parameters. On the other hand, in such cases, we
* can check, if there is a corresponding U:Line or H:Line given
* with the load directive in files { } section of the new ircd.conf
* Then we can append the needed flags to the new structure.
* -TimeMr14C
*/
target_p = make_client(client_p);
make_server(target_p);
target_p->hopcount = hop;
target_p->protoflags |= flags;
strlcpy_irc(target_p->name, name, HOSTLEN);
strlcpy_irc(target_p->info, info, REALLEN);
target_p->servptr = source_p;
SetServer(target_p);
Count.server++;
GeneralOpts.split = 0;
if (IsULine(source_p)) {
target_p->protoflags |= PFLAGS_ULINE;
sendto_gnotice("%s introducing U:lined server %s", client_p->name, target_p->name);
}
add_client_to_list(target_p);
add_server_to_list(target_p);
add_to_client_hash_table(target_p->name, target_p);
add_client_to_llist(&(target_p->servptr->serv->servers), target_p);
target_p->servptr->serv->servercnt++;
if (sid)
add_base64_server(target_p, sid);
/*
* Old sendto_serv_but_one() call removed because we now
* need to send different names to different servers
* (domain name matching)
*/
thisdata.client_p = client_p;
thisdata.source_p = source_p;
thisdata.aclient_p = target_p;
thisdata.name = name;
thisdata.check = hop;
hook_call_event(hookid_inform_remote_servers, &thisdata);
sendto_gnotice("Server %s being introduced by %s", target_p->name, source_p->name);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1