1378 lines
36 KiB
C
1378 lines
36 KiB
C
/*
|
|
* ircd-ratbox: A slightly useful ircd.
|
|
* m_server.c: Introduces a server.
|
|
*
|
|
* Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
|
|
* Copyright (C) 1996-2002 Hybrid Development Team
|
|
* Copyright (C) 2002-2012 ircd-ratbox development team
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
|
* USA
|
|
*
|
|
* $Id: m_server.c 28677 2015-09-29 17:51:20Z androsyn $
|
|
*/
|
|
|
|
#include "stdinc.h"
|
|
#include "struct.h"
|
|
#include "client.h" /* client struct */
|
|
#include "channel.h"
|
|
#include "hash.h" /* add_to_client_hash */
|
|
#include "match.h"
|
|
#include "ircd.h" /* me */
|
|
#include "s_conf.h" /* struct ConfItem */
|
|
#include "s_newconf.h"
|
|
#include "s_log.h" /* log level defines */
|
|
#include "s_serv.h" /* server_estab, check_server */
|
|
#include "scache.h" /* find_or_add */
|
|
#include "send.h" /* sendto_one */
|
|
#include "parse.h"
|
|
#include "hook.h"
|
|
#include "modules.h"
|
|
#include "s_stats.h"
|
|
#include "packet.h"
|
|
#include "s_user.h"
|
|
#include "reject.h"
|
|
#include "sslproc.h"
|
|
|
|
static int mr_server(struct Client *, struct Client *, int, const char **);
|
|
static int ms_server(struct Client *, struct Client *, int, const char **);
|
|
static int ms_sid(struct Client *, struct Client *, int, const char **);
|
|
|
|
struct Message server_msgtab = {
|
|
"SERVER", 0, 0, 0, MFLG_SLOW | MFLG_UNREG,
|
|
{{mr_server, 4}, mg_reg, mg_ignore, {ms_server, 4}, mg_ignore, mg_reg}
|
|
};
|
|
|
|
struct Message sid_msgtab = {
|
|
"SID", 0, 0, 0, MFLG_SLOW,
|
|
{mg_ignore, mg_reg, mg_ignore, {ms_sid, 5}, mg_ignore, mg_reg}
|
|
};
|
|
|
|
mapi_clist_av1 server_clist[] = { &server_msgtab, &sid_msgtab, NULL };
|
|
|
|
DECLARE_MODULE_AV1(server, NULL, NULL, server_clist, NULL, NULL, "$Revision: 28677 $");
|
|
|
|
static struct Client *server_exists(const char *);
|
|
static int set_server_gecos(struct Client *, const char *);
|
|
|
|
static int check_server(const char *name, struct Client *client_p);
|
|
static int server_estab(struct Client *client_p);
|
|
|
|
|
|
/* enums for check_server */
|
|
enum
|
|
{
|
|
NO_NLINE = -1,
|
|
INVALID_PASS = -2,
|
|
INVALID_HOST = -3,
|
|
INVALID_SERVERNAME = -4,
|
|
NEED_SSL = -5,
|
|
};
|
|
|
|
|
|
/*
|
|
* mr_server - SERVER message handler
|
|
* parv[0] = sender prefix
|
|
* parv[1] = servername
|
|
* parv[2] = serverinfo/hopcount
|
|
* parv[3] = serverinfo
|
|
*/
|
|
static int
|
|
mr_server(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
|
|
{
|
|
char info[REALLEN + 1];
|
|
const char *name;
|
|
struct Client *target_p;
|
|
int hop;
|
|
|
|
name = parv[1];
|
|
hop = atoi(parv[2]);
|
|
rb_strlcpy(info, parv[3], sizeof(info));
|
|
|
|
/*
|
|
* Reject a direct nonTS server connection if we're TS_ONLY -orabidoo
|
|
*/
|
|
if(!DoesTS(client_p))
|
|
{
|
|
exit_client(client_p, client_p, client_p, "Non-TS server");
|
|
return 0;
|
|
}
|
|
|
|
if(!valid_servername(name))
|
|
{
|
|
exit_client(client_p, client_p, client_p, "Invalid servername.");
|
|
return 0;
|
|
}
|
|
|
|
/* Now we just have to call check_server and everything should be
|
|
* check for us... -A1kmm. */
|
|
switch (check_server(name, client_p))
|
|
{
|
|
case NO_NLINE:
|
|
if(ConfigFileEntry.warn_no_nline)
|
|
{
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Unauthorised server connection attempt from [@255.255.255.255]: "
|
|
"No entry for servername %s", name);
|
|
|
|
ilog(L_SERVER, "Access denied, No N line for server %s",
|
|
log_client_name(client_p, SHOW_IP));
|
|
}
|
|
|
|
exit_client(client_p, client_p, client_p, "Invalid servername.");
|
|
return 0;
|
|
/* NOT REACHED */
|
|
break;
|
|
|
|
case INVALID_PASS:
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Unauthorised server connection attempt from [@255.255.255.255]: "
|
|
"Bad password for server %s", name);
|
|
|
|
ilog(L_SERVER, "Access denied, invalid password for server %s",
|
|
log_client_name(client_p, SHOW_IP));
|
|
|
|
exit_client(client_p, client_p, client_p, "Invalid password.");
|
|
return 0;
|
|
case INVALID_HOST:
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Unauthorised server connection attempt from [@255.255.255.255]: "
|
|
"Invalid host for server %s", name);
|
|
|
|
ilog(L_SERVER, "Access denied, invalid host for server %s",
|
|
log_client_name(client_p, SHOW_IP));
|
|
|
|
exit_client(client_p, client_p, client_p, "Invalid host.");
|
|
return 0;
|
|
/* servername is > HOSTLEN */
|
|
case INVALID_SERVERNAME:
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Invalid servername %s from [@255.255.255.255]",
|
|
client_p->name);
|
|
ilog(L_SERVER, "Access denied, invalid servername from %s",
|
|
log_client_name(client_p, SHOW_IP));
|
|
|
|
exit_client(client_p, client_p, client_p, "Invalid servername.");
|
|
return 0;
|
|
case NEED_SSL:
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Connection from servername %s requires SSL/TLS but is plaintext",
|
|
name);
|
|
ilog(L_SERVER, "Access denied, requires SSL/TLS but is plaintext from %s",
|
|
log_client_name(client_p, SHOW_IP));
|
|
|
|
exit_client(client_p, client_p, client_p,
|
|
"Access denied, requires SSL/TLS but is plaintext");
|
|
return 0;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* require TS6 for direct links */
|
|
if(!IsCapable(client_p, CAP_TS6))
|
|
{
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s dropped, TS6 protocol is required", name);
|
|
exit_client(client_p, client_p, client_p, "Incompatible TS version");
|
|
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
|
|
*
|
|
* Definitely don't do that here. This is from an unregistered
|
|
* connect - A1kmm.
|
|
*/
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Attempt to re-introduce server %s from [@255.255.255.255]",
|
|
name);
|
|
ilog(L_SERVER, "Attempt to re-introduce server %s from %s",
|
|
name, log_client_name(client_p, SHOW_IP));
|
|
|
|
sendto_one(client_p, "ERROR :Server already exists.");
|
|
exit_client(client_p, client_p, client_p, "Server Exists");
|
|
return 0;
|
|
}
|
|
|
|
if(has_id(client_p) && (target_p = find_id(client_p->id)) != NULL)
|
|
{
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Attempt to re-introduce SID %s from %s[@255.255.255.255]",
|
|
client_p->id, name);
|
|
ilog(L_SERVER, "Attempt to re-introduce SID %s from %s",
|
|
name, log_client_name(client_p, SHOW_IP));
|
|
|
|
sendto_one(client_p, "ERROR :SID already exists.");
|
|
exit_client(client_p, client_p, client_p, "SID Exists");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* if we are connecting (Handshake), we already have the name from the
|
|
* C:line in client_p->name
|
|
*/
|
|
|
|
client_p->name = scache_add(name);
|
|
set_server_gecos(client_p, info);
|
|
client_p->hopcount = hop;
|
|
server_estab(client_p);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* ms_server - SERVER message handler
|
|
* parv[0] = sender prefix
|
|
* parv[1] = servername
|
|
* parv[2] = serverinfo/hopcount
|
|
* parv[3] = serverinfo
|
|
*/
|
|
static int
|
|
ms_server(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
|
|
{
|
|
char info[REALLEN + 1];
|
|
/* same size as in s_misc.c */
|
|
const char *name;
|
|
struct Client *target_p;
|
|
struct remote_conf *hub_p;
|
|
hook_data_client hdata;
|
|
int hop;
|
|
int hlined = 0;
|
|
int llined = 0;
|
|
rb_dlink_node *ptr;
|
|
|
|
name = parv[1];
|
|
hop = atoi(parv[2]);
|
|
rb_strlcpy(info, parv[3], sizeof(info));
|
|
|
|
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(irccmp(target_p->name, name) && target_p->from == client_p)
|
|
return 0;
|
|
|
|
sendto_one(client_p, "ERROR :Server %s already exists", name);
|
|
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s cancelled, server %s already exists",
|
|
client_p->name, name);
|
|
ilog(L_SERVER, "Link %s cancelled, server %s already exists", client_p->name, name);
|
|
|
|
exit_client(client_p, client_p, &me, "Server Exists");
|
|
return 0;
|
|
}
|
|
|
|
if(!valid_servername(name) || strlen(name) > HOSTLEN)
|
|
{
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s introduced server with invalid servername %s",
|
|
client_p->name, name);
|
|
ilog(L_SERVER, "Link %s introduced with invalid servername %s", client_p->name,
|
|
name);
|
|
exit_client(NULL, client_p, &me, "Invalid servername introduced.");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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 || EmptyString(info))
|
|
{
|
|
sendto_one(client_p, "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.
|
|
*
|
|
*/
|
|
RB_DLINK_FOREACH(ptr, hubleaf_conf_list.head)
|
|
{
|
|
hub_p = ptr->data;
|
|
|
|
if(match(hub_p->server, client_p->name) && match(hub_p->host, name))
|
|
{
|
|
if(hub_p->flags & CONF_HUB)
|
|
hlined++;
|
|
else
|
|
llined++;
|
|
}
|
|
}
|
|
|
|
/* Ok, this way this works is
|
|
*
|
|
* A server can have a CONF_HUB allowing it to introduce servers
|
|
* behind it.
|
|
*
|
|
* connect {
|
|
* name = "irc.bighub.net";
|
|
* hub_mask="*";
|
|
* ...
|
|
*
|
|
* That would allow "irc.bighub.net" to introduce anything it wanted..
|
|
*
|
|
* However
|
|
*
|
|
* connect {
|
|
* name = "irc.somehub.fi";
|
|
* hub_mask="*";
|
|
* leaf_mask="*.edu";
|
|
*...
|
|
* Would allow this server in finland to hub anything but
|
|
* .edu's
|
|
*/
|
|
|
|
/* Ok, check client_p can hub the new server, and make sure it's not a LL */
|
|
if(!hlined)
|
|
{
|
|
/* OOOPs nope can't HUB */
|
|
sendto_realops_flags(UMODE_ALL, L_ALL, "Non-Hub link %s introduced %s.",
|
|
client_p->name, name);
|
|
ilog(L_SERVER, "Non-Hub link %s introduced %s.", client_p->name, name);
|
|
exit_client(NULL, client_p, &me, "No matching hub_mask.");
|
|
return 0;
|
|
}
|
|
|
|
/* Check for the new server being leafed behind this HUB */
|
|
if(llined)
|
|
{
|
|
/* OOOPs nope can't HUB this leaf */
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s introduced leafed server %s.", client_p->name, name);
|
|
ilog(L_SERVER, "Link %s introduced leafed server %s.", client_p->name, name);
|
|
exit_client(NULL, client_p, &me, "Leafed Server.");
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
target_p = make_client(client_p);
|
|
make_server(target_p);
|
|
target_p->hopcount = hop;
|
|
target_p->name = scache_add(name);
|
|
|
|
set_server_gecos(target_p, info);
|
|
|
|
target_p->servptr = source_p;
|
|
|
|
SetServer(target_p);
|
|
|
|
rb_dlinkAddTail(target_p, &target_p->node, &global_client_list);
|
|
rb_dlinkAddTailAlloc(target_p, &global_serv_list);
|
|
add_to_hash(HASH_CLIENT, target_p->name, target_p);
|
|
rb_dlinkAdd(target_p, &target_p->lnode, &target_p->servptr->serv->servers);
|
|
|
|
sendto_server(client_p, NULL, NOCAPS, NOCAPS,
|
|
":%s SERVER %s %d :%s%s",
|
|
source_p->name, target_p->name, target_p->hopcount + 1,
|
|
IsHidden(target_p) ? "(H) " : "", target_p->info);
|
|
|
|
sendto_realops_flags(UMODE_EXTERNAL, L_ALL,
|
|
"Server %s being introduced by %s", target_p->name, source_p->name);
|
|
|
|
/* quick, dirty EOB. you know you love it. */
|
|
sendto_one(target_p, ":%s PING %s %s", get_id(&me, target_p), me.name, target_p->name);
|
|
|
|
hdata.client = source_p;
|
|
hdata.target = target_p;
|
|
call_hook(h_server_introduced, &hdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ms_sid(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
|
|
{
|
|
struct Client *target_p;
|
|
struct remote_conf *hub_p;
|
|
hook_data_client hdata;
|
|
rb_dlink_node *ptr;
|
|
int hop;
|
|
int hlined = 0;
|
|
int llined = 0;
|
|
|
|
hop = atoi(parv[2]);
|
|
|
|
/* collision on the name? */
|
|
if((target_p = server_exists(parv[1])) != NULL)
|
|
{
|
|
sendto_one(client_p, "ERROR :Server %s already exists", parv[1]);
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s cancelled, server %s already exists",
|
|
client_p->name, parv[1]);
|
|
ilog(L_SERVER, "Link %s cancelled, server %s already exists",
|
|
client_p->name, parv[1]);
|
|
exit_client(NULL, client_p, &me, "Server Exists");
|
|
return 0;
|
|
}
|
|
|
|
/* collision on the SID? */
|
|
if((target_p = find_id(parv[3])) != NULL)
|
|
{
|
|
sendto_one(client_p, "ERROR :SID %s already exists", parv[3]);
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s cancelled, SID %s already exists",
|
|
client_p->name, parv[3]);
|
|
ilog(L_SERVER, "Link %s cancelled, SID %s already exists", client_p->name, parv[3]);
|
|
exit_client(NULL, client_p, &me, "SID Exists");
|
|
return 0;
|
|
}
|
|
|
|
if(!valid_servername(parv[1]) || strlen(parv[1]) > HOSTLEN)
|
|
{
|
|
sendto_one(client_p, "ERROR :Invalid servername");
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s cancelled, servername %s invalid",
|
|
client_p->name, parv[1]);
|
|
ilog(L_SERVER, "Link %s cancelled, servername %s invalid", client_p->name, parv[1]);
|
|
exit_client(NULL, client_p, &me, "Bogus server name");
|
|
return 0;
|
|
}
|
|
|
|
if(!IsDigit(parv[3][0]) || !IsIdChar(parv[3][1]) ||
|
|
!IsIdChar(parv[3][2]) || parv[3][3] != '\0')
|
|
{
|
|
sendto_one(client_p, "ERROR :Invalid SID");
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s cancelled, SID %s invalid", client_p->name, parv[3]);
|
|
ilog(L_SERVER, "Link %s cancelled, SID %s invalid", client_p->name, parv[3]);
|
|
exit_client(NULL, client_p, &me, "Bogus SID");
|
|
return 0;
|
|
}
|
|
|
|
/* for the directly connected server:
|
|
* H: allows it to introduce a server matching that mask
|
|
* L: disallows it introducing a server matching that mask
|
|
*/
|
|
RB_DLINK_FOREACH(ptr, hubleaf_conf_list.head)
|
|
{
|
|
hub_p = ptr->data;
|
|
|
|
if(match(hub_p->server, client_p->name) && match(hub_p->host, parv[1]))
|
|
{
|
|
if(hub_p->flags & CONF_HUB)
|
|
hlined++;
|
|
else
|
|
llined++;
|
|
}
|
|
}
|
|
|
|
/* no matching hub_mask */
|
|
if(!hlined)
|
|
{
|
|
sendto_one(client_p, "ERROR :No matching hub_mask");
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Non-Hub link %s introduced %s.", client_p->name, parv[1]);
|
|
ilog(L_SERVER, "Non-Hub link %s introduced %s.", client_p->name, parv[1]);
|
|
exit_client(NULL, client_p, &me, "No matching hub_mask.");
|
|
return 0;
|
|
}
|
|
|
|
/* matching leaf_mask */
|
|
if(llined)
|
|
{
|
|
sendto_one(client_p, "ERROR :Matching leaf_mask");
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s introduced leafed server %s.",
|
|
client_p->name, parv[1]);
|
|
ilog(L_SERVER, "Link %s introduced leafed server %s.", client_p->name, parv[1]);
|
|
exit_client(NULL, client_p, &me, "Leafed Server.");
|
|
return 0;
|
|
}
|
|
|
|
/* ok, alls good */
|
|
target_p = make_client(client_p);
|
|
make_server(target_p);
|
|
|
|
target_p->name = scache_add(parv[1]);
|
|
|
|
target_p->hopcount = atoi(parv[2]);
|
|
strcpy(target_p->id, parv[3]);
|
|
set_server_gecos(target_p, parv[4]);
|
|
|
|
target_p->servptr = source_p;
|
|
SetServer(target_p);
|
|
|
|
rb_dlinkAddTail(target_p, &target_p->node, &global_client_list);
|
|
rb_dlinkAddTailAlloc(target_p, &global_serv_list);
|
|
add_to_hash(HASH_CLIENT, target_p->name, target_p);
|
|
add_to_hash(HASH_ID, target_p->id, target_p);
|
|
rb_dlinkAdd(target_p, &target_p->lnode, &target_p->servptr->serv->servers);
|
|
|
|
sendto_server(client_p, NULL, CAP_TS6, NOCAPS,
|
|
":%s SID %s %d %s :%s%s",
|
|
source_p->id, target_p->name, target_p->hopcount + 1,
|
|
target_p->id, IsHidden(target_p) ? "(H) " : "", target_p->info);
|
|
sendto_server(client_p, NULL, NOCAPS, CAP_TS6,
|
|
":%s SERVER %s %d :%s%s",
|
|
source_p->name, target_p->name, target_p->hopcount + 1,
|
|
IsHidden(target_p) ? "(H) " : "", target_p->info);
|
|
|
|
sendto_realops_flags(UMODE_EXTERNAL, L_ALL,
|
|
"Server %s being introduced by %s", target_p->name, source_p->name);
|
|
|
|
/* quick, dirty EOB. you know you love it. */
|
|
sendto_one(target_p, ":%s PING %s %s",
|
|
get_id(&me, target_p), me.name, get_id(target_p, target_p));
|
|
|
|
hdata.client = source_p;
|
|
hdata.target = target_p;
|
|
call_hook(h_server_introduced, &hdata);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* set_server_gecos()
|
|
*
|
|
* input - pointer to client
|
|
* output - none
|
|
* side effects - servers gecos field is set
|
|
*/
|
|
static int
|
|
set_server_gecos(struct Client *client_p, const char *info)
|
|
{
|
|
/* check the info for [IP] */
|
|
if(info[0])
|
|
{
|
|
char *p;
|
|
char *s;
|
|
char *t;
|
|
|
|
s = LOCAL_COPY(info);
|
|
|
|
/* we should only check the first word for an ip */
|
|
if((p = strchr(s, ' ')))
|
|
*p = '\0';
|
|
|
|
/* check for a ] which would symbolise an [IP] */
|
|
if((t = strchr(s, ']')))
|
|
{
|
|
/* set s to after the first space */
|
|
if(p)
|
|
s = ++p;
|
|
else
|
|
s = NULL;
|
|
}
|
|
/* no ], put the space back */
|
|
else if(p)
|
|
*p = ' ';
|
|
|
|
/* p may have been set to a trailing space, so check s exists and that
|
|
* it isnt \0 */
|
|
if(s && (*s != '\0'))
|
|
{
|
|
/* a space? if not (H) could be the last part of info.. */
|
|
if((p = strchr(s, ' ')))
|
|
*p = '\0';
|
|
|
|
/* check for (H) which is a hidden server */
|
|
if(!strcmp(s, "(H)"))
|
|
{
|
|
SetHidden(client_p);
|
|
|
|
/* if there was no space.. theres nothing to set info to */
|
|
if(p)
|
|
s = ++p;
|
|
else
|
|
s = NULL;
|
|
}
|
|
else if(p)
|
|
*p = ' ';
|
|
|
|
/* if there was a trailing space, s could point to \0, so check */
|
|
if(s && (*s != '\0'))
|
|
{
|
|
rb_strlcpy(client_p->info, s, sizeof(client_p->info));
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
rb_strlcpy(client_p->info, "(Unknown Location)", sizeof(client_p->info));
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* server_exists()
|
|
*
|
|
* inputs - servername
|
|
* output - 1 if server exists, 0 if doesnt exist
|
|
*/
|
|
static struct Client *
|
|
server_exists(const char *servername)
|
|
{
|
|
struct Client *target_p;
|
|
rb_dlink_node *ptr;
|
|
|
|
RB_DLINK_FOREACH(ptr, global_serv_list.head)
|
|
{
|
|
target_p = ptr->data;
|
|
|
|
if(match(target_p->name, servername) || match(servername, target_p->name))
|
|
return target_p;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int
|
|
check_server(const char *name, struct Client *client_p)
|
|
{
|
|
struct server_conf *server_p = NULL;
|
|
struct server_conf *tmp_p;
|
|
rb_dlink_node *ptr;
|
|
int error = NO_NLINE;
|
|
|
|
s_assert(NULL != client_p);
|
|
if(client_p == NULL)
|
|
return error;
|
|
|
|
if(!(client_p->localClient->passwd))
|
|
return INVALID_PASS;
|
|
|
|
if(strlen(name) > HOSTLEN)
|
|
return INVALID_SERVERNAME;
|
|
|
|
RB_DLINK_FOREACH(ptr, server_conf_list.head)
|
|
{
|
|
tmp_p = ptr->data;
|
|
|
|
if(ServerConfIllegal(tmp_p))
|
|
continue;
|
|
|
|
if(!match(name, tmp_p->name))
|
|
continue;
|
|
|
|
error = INVALID_HOST;
|
|
|
|
/* XXX: Fix me for IPv6 */
|
|
/* XXX sockhost is the IPv4 ip as a string */
|
|
if(match(tmp_p->host, client_p->host) || match(tmp_p->host, client_p->sockhost))
|
|
{
|
|
error = INVALID_PASS;
|
|
|
|
if(ServerConfEncrypted(tmp_p))
|
|
{
|
|
if(!strcmp(tmp_p->passwd, rb_crypt(client_p->localClient->passwd,
|
|
tmp_p->passwd)))
|
|
{
|
|
server_p = tmp_p;
|
|
break;
|
|
}
|
|
}
|
|
else if(!strcmp(tmp_p->passwd, client_p->localClient->passwd))
|
|
{
|
|
server_p = tmp_p;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(server_p == NULL)
|
|
return error;
|
|
|
|
if(ServerConfSSL(server_p) && client_p->localClient->ssl_ctl == NULL)
|
|
{
|
|
return NEED_SSL;
|
|
}
|
|
|
|
attach_server_conf(client_p, server_p);
|
|
|
|
/* clear ZIP/TB if they support but we dont want them */
|
|
if(!ServerConfCompressed(server_p))
|
|
ClearCap(client_p, CAP_ZIP);
|
|
|
|
if(!ServerConfTb(server_p))
|
|
ClearCap(client_p, CAP_TB);
|
|
|
|
#ifdef RB_IPV6
|
|
if(GET_SS_FAMILY(&client_p->localClient->ip) == AF_INET6)
|
|
{
|
|
if(IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)&server_p->ipnum)->sin6_addr))
|
|
{
|
|
memcpy(&((struct sockaddr_in6 *)&server_p->ipnum)->sin6_addr,
|
|
&((struct sockaddr_in6 *)&client_p->localClient->ip)->sin6_addr,
|
|
sizeof(struct in6_addr));
|
|
SET_SS_LEN(&server_p->ipnum, sizeof(struct sockaddr_in6));
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if(((struct sockaddr_in *)&server_p->ipnum)->sin_addr.s_addr == INADDR_NONE)
|
|
{
|
|
((struct sockaddr_in *)&server_p->ipnum)->sin_addr.s_addr =
|
|
((struct sockaddr_in *)&client_p->localClient->ip)->sin_addr.s_addr;
|
|
}
|
|
SET_SS_LEN(&server_p->ipnum, sizeof(struct sockaddr_in));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* burst_modes_TS5()
|
|
*
|
|
* input - client to burst to, channel name, list to burst, mode flag
|
|
* output -
|
|
* side effects - client is sent a list of +b, or +e, or +I modes
|
|
*/
|
|
static void
|
|
burst_modes_TS5(struct Client *client_p, char *chname, rb_dlink_list *list, char flag)
|
|
{
|
|
char buf[BUFSIZE];
|
|
char mbuf[MODEBUFLEN];
|
|
char pbuf[BUFSIZE];
|
|
struct Ban *banptr;
|
|
rb_dlink_node *ptr;
|
|
int tlen;
|
|
int mlen;
|
|
int cur_len;
|
|
char *mp;
|
|
char *pp;
|
|
int count = 0;
|
|
|
|
mlen = rb_sprintf(buf, ":%s MODE %s +", me.name, chname);
|
|
cur_len = mlen;
|
|
|
|
mp = mbuf;
|
|
pp = pbuf;
|
|
|
|
RB_DLINK_FOREACH(ptr, list->head)
|
|
{
|
|
banptr = ptr->data;
|
|
tlen = strlen(banptr->banstr) + 3;
|
|
|
|
/* uh oh */
|
|
if(tlen > MODEBUFLEN)
|
|
continue;
|
|
|
|
if((count >= MAXMODEPARAMS) || ((cur_len + tlen + 2) > (BUFSIZE - 3)))
|
|
{
|
|
sendto_one(client_p, "%s%s %s", buf, mbuf, pbuf);
|
|
|
|
mp = mbuf;
|
|
pp = pbuf;
|
|
cur_len = mlen;
|
|
count = 0;
|
|
}
|
|
|
|
*mp++ = flag;
|
|
*mp = '\0';
|
|
pp += rb_sprintf(pp, "%s ", banptr->banstr);
|
|
cur_len += tlen;
|
|
count++;
|
|
}
|
|
|
|
if(count != 0)
|
|
sendto_one(client_p, "%s%s %s", buf, mbuf, pbuf);
|
|
}
|
|
|
|
/* burst_modes_TS6()
|
|
*
|
|
* input - client to burst to, channel name, list to burst, mode flag
|
|
* output -
|
|
* side effects - client is sent a list of +b, +e, or +I modes
|
|
*/
|
|
static void
|
|
burst_modes_TS6(struct Client *client_p, struct Channel *chptr, rb_dlink_list *list, char flag)
|
|
{
|
|
char buf[BUFSIZE];
|
|
rb_dlink_node *ptr;
|
|
struct Ban *banptr;
|
|
char *t;
|
|
int tlen;
|
|
int mlen;
|
|
int cur_len;
|
|
|
|
cur_len = mlen = rb_sprintf(buf, ":%s BMASK %" RBTT_FMT " %s %c :",
|
|
me.id, chptr->channelts, chptr->chname, flag);
|
|
t = buf + mlen;
|
|
|
|
RB_DLINK_FOREACH(ptr, list->head)
|
|
{
|
|
banptr = ptr->data;
|
|
|
|
tlen = strlen(banptr->banstr) + 1;
|
|
|
|
/* uh oh */
|
|
if(cur_len + tlen > BUFSIZE - 3)
|
|
{
|
|
/* the one we're trying to send doesnt fit at all! */
|
|
if(cur_len == mlen)
|
|
{
|
|
s_assert(0);
|
|
continue;
|
|
}
|
|
|
|
/* chop off trailing space and send.. */
|
|
*(t - 1) = '\0';
|
|
sendto_one_buffer(client_p, buf);
|
|
cur_len = mlen;
|
|
t = buf + mlen;
|
|
}
|
|
|
|
rb_sprintf(t, "%s ", banptr->banstr);
|
|
t += tlen;
|
|
cur_len += tlen;
|
|
}
|
|
|
|
/* cant ever exit the loop above without having modified buf,
|
|
* chop off trailing space and send.
|
|
*/
|
|
*(t - 1) = '\0';
|
|
sendto_one_buffer(client_p, buf);
|
|
}
|
|
|
|
/*
|
|
* burst_TS5
|
|
*
|
|
* inputs - client (server) to send nick towards
|
|
* - client to send nick for
|
|
* output - NONE
|
|
* side effects - NICK message is sent towards given client_p
|
|
*/
|
|
static void
|
|
burst_TS5(struct Client *client_p)
|
|
{
|
|
static char ubuf[12];
|
|
char buf[BUFSIZE];
|
|
struct Client *target_p;
|
|
struct Channel *chptr;
|
|
struct membership *msptr;
|
|
hook_data_client hclientinfo;
|
|
hook_data_channel hchaninfo;
|
|
rb_dlink_node *ptr;
|
|
rb_dlink_node *uptr;
|
|
char *t;
|
|
int tlen, mlen;
|
|
int cur_len = 0;
|
|
|
|
hclientinfo.client = hchaninfo.client = client_p;
|
|
|
|
RB_DLINK_FOREACH(ptr, global_client_list.head)
|
|
{
|
|
target_p = ptr->data;
|
|
|
|
if(!IsClient(target_p))
|
|
continue;
|
|
|
|
send_umode(NULL, target_p, 0, SEND_UMODES, ubuf);
|
|
if(!*ubuf)
|
|
{
|
|
ubuf[0] = '+';
|
|
ubuf[1] = '\0';
|
|
}
|
|
|
|
sendto_one(client_p, "NICK %s %d %" RBTT_FMT " %s %s %s %s :%s",
|
|
target_p->name, target_p->hopcount + 1,
|
|
target_p->tsinfo, ubuf,
|
|
target_p->username, target_p->host,
|
|
target_p->servptr->name, target_p->info);
|
|
|
|
if(ConfigFileEntry.burst_away && !EmptyString(target_p->user->away))
|
|
sendto_one(client_p, ":%s AWAY :%s", target_p->name, target_p->user->away);
|
|
|
|
hclientinfo.target = target_p;
|
|
call_hook(h_burst_client, &hclientinfo);
|
|
}
|
|
|
|
RB_DLINK_FOREACH(ptr, global_channel_list.head)
|
|
{
|
|
chptr = ptr->data;
|
|
|
|
s_assert(rb_dlink_list_length(&chptr->members) > 0);
|
|
if(rb_dlink_list_length(&chptr->members) <= 0)
|
|
continue;
|
|
|
|
if(*chptr->chname != '#')
|
|
continue;
|
|
|
|
cur_len = mlen = rb_sprintf(buf, ":%s SJOIN %" RBTT_FMT " %s %s :", me.name,
|
|
chptr->channelts, chptr->chname,
|
|
channel_modes(chptr, client_p));
|
|
|
|
t = buf + mlen;
|
|
|
|
RB_DLINK_FOREACH(uptr, chptr->members.head)
|
|
{
|
|
msptr = uptr->data;
|
|
|
|
tlen = strlen(msptr->client_p->name) + 1;
|
|
if(is_chanop(msptr))
|
|
tlen++;
|
|
if(is_voiced(msptr))
|
|
tlen++;
|
|
|
|
if(cur_len + tlen >= BUFSIZE - 3)
|
|
{
|
|
t--;
|
|
*t = '\0';
|
|
sendto_one_buffer(client_p, buf);
|
|
cur_len = mlen;
|
|
t = buf + mlen;
|
|
}
|
|
|
|
rb_sprintf(t, "%s%s ", find_channel_status(msptr, 1),
|
|
msptr->client_p->name);
|
|
|
|
cur_len += tlen;
|
|
t += tlen;
|
|
}
|
|
|
|
/* remove trailing space */
|
|
t--;
|
|
*t = '\0';
|
|
sendto_one_buffer(client_p, buf);
|
|
|
|
burst_modes_TS5(client_p, chptr->chname, &chptr->banlist, 'b');
|
|
|
|
if(IsCapable(client_p, CAP_EX))
|
|
burst_modes_TS5(client_p, chptr->chname, &chptr->exceptlist, 'e');
|
|
|
|
if(IsCapable(client_p, CAP_IE))
|
|
burst_modes_TS5(client_p, chptr->chname, &chptr->invexlist, 'I');
|
|
|
|
if(IsCapable(client_p, CAP_TB) && chptr->topic != NULL)
|
|
sendto_one(client_p, ":%s TB %s %" RBTT_FMT " %s%s:%s",
|
|
me.name, chptr->chname, chptr->topic->topic_time,
|
|
ConfigChannel.burst_topicwho ? chptr->topic->topic_info : "",
|
|
ConfigChannel.burst_topicwho ? " " : "", chptr->topic->topic);
|
|
|
|
hchaninfo.chptr = chptr;
|
|
call_hook(h_burst_channel, &hchaninfo);
|
|
}
|
|
|
|
hclientinfo.target = NULL;
|
|
call_hook(h_burst_finished, &hclientinfo);
|
|
}
|
|
|
|
/*
|
|
* burst_TS6
|
|
*
|
|
* inputs - client (server) to send nick towards
|
|
* - client to send nick for
|
|
* output - NONE
|
|
* side effects - NICK message is sent towards given client_p
|
|
*/
|
|
static void
|
|
burst_TS6(struct Client *client_p)
|
|
{
|
|
static char ubuf[12];
|
|
char buf[BUFSIZE];
|
|
struct Client *target_p;
|
|
struct Channel *chptr;
|
|
struct membership *msptr;
|
|
hook_data_client hclientinfo;
|
|
hook_data_channel hchaninfo;
|
|
rb_dlink_node *ptr;
|
|
rb_dlink_node *uptr;
|
|
char *t;
|
|
int tlen, mlen;
|
|
int cur_len = 0;
|
|
|
|
hclientinfo.client = hchaninfo.client = client_p;
|
|
|
|
RB_DLINK_FOREACH(ptr, global_client_list.head)
|
|
{
|
|
target_p = ptr->data;
|
|
|
|
if(!IsClient(target_p))
|
|
continue;
|
|
|
|
send_umode(NULL, target_p, 0, SEND_UMODES, ubuf);
|
|
if(!*ubuf)
|
|
{
|
|
ubuf[0] = '+';
|
|
ubuf[1] = '\0';
|
|
}
|
|
|
|
if(has_id(target_p))
|
|
sendto_one(client_p, ":%s UID %s %d %" RBTT_FMT " %s %s %s %s %s :%s",
|
|
target_p->servptr->id, target_p->name,
|
|
target_p->hopcount + 1,
|
|
target_p->tsinfo, ubuf,
|
|
target_p->username, target_p->host,
|
|
IsIPSpoof(target_p) ? "0" : target_p->sockhost,
|
|
target_p->id, target_p->info);
|
|
else
|
|
sendto_one(client_p, "NICK %s %d %" RBTT_FMT " %s %s %s %s :%s",
|
|
target_p->name,
|
|
target_p->hopcount + 1,
|
|
target_p->tsinfo,
|
|
ubuf,
|
|
target_p->username, target_p->host,
|
|
target_p->servptr->name, target_p->info);
|
|
|
|
if(ConfigFileEntry.burst_away && !EmptyString(target_p->user->away))
|
|
sendto_one(client_p, ":%s AWAY :%s",
|
|
use_id(target_p), target_p->user->away);
|
|
|
|
hclientinfo.target = target_p;
|
|
call_hook(h_burst_client, &hclientinfo);
|
|
}
|
|
|
|
RB_DLINK_FOREACH(ptr, global_channel_list.head)
|
|
{
|
|
chptr = ptr->data;
|
|
|
|
s_assert(rb_dlink_list_length(&chptr->members) > 0);
|
|
if(rb_dlink_list_length(&chptr->members) <= 0)
|
|
continue;
|
|
|
|
if(*chptr->chname != '#')
|
|
continue;
|
|
|
|
cur_len = mlen = rb_sprintf(buf, ":%s SJOIN %" RBTT_FMT " %s %s :", me.id,
|
|
chptr->channelts, chptr->chname,
|
|
channel_modes(chptr, client_p));
|
|
|
|
t = buf + mlen;
|
|
|
|
RB_DLINK_FOREACH(uptr, chptr->members.head)
|
|
{
|
|
msptr = uptr->data;
|
|
|
|
tlen = strlen(use_id(msptr->client_p)) + 1;
|
|
if(is_chanop(msptr))
|
|
tlen++;
|
|
if(is_voiced(msptr))
|
|
tlen++;
|
|
|
|
if(cur_len + tlen >= BUFSIZE - 3)
|
|
{
|
|
*(t - 1) = '\0';
|
|
sendto_one_buffer(client_p, buf);
|
|
cur_len = mlen;
|
|
t = buf + mlen;
|
|
}
|
|
|
|
rb_sprintf(t, "%s%s ", find_channel_status(msptr, 1),
|
|
use_id(msptr->client_p));
|
|
|
|
cur_len += tlen;
|
|
t += tlen;
|
|
}
|
|
|
|
/* remove trailing space */
|
|
*(t - 1) = '\0';
|
|
sendto_one_buffer(client_p, buf);
|
|
|
|
if(rb_dlink_list_length(&chptr->banlist) > 0)
|
|
burst_modes_TS6(client_p, chptr, &chptr->banlist, 'b');
|
|
|
|
if(IsCapable(client_p, CAP_EX) && rb_dlink_list_length(&chptr->exceptlist) > 0)
|
|
burst_modes_TS6(client_p, chptr, &chptr->exceptlist, 'e');
|
|
|
|
if(IsCapable(client_p, CAP_IE) && rb_dlink_list_length(&chptr->invexlist) > 0)
|
|
burst_modes_TS6(client_p, chptr, &chptr->invexlist, 'I');
|
|
|
|
if(IsCapable(client_p, CAP_TB) && chptr->topic != NULL)
|
|
sendto_one(client_p, ":%s TB %s %" RBTT_FMT " %s%s:%s",
|
|
me.id, chptr->chname, chptr->topic->topic_time,
|
|
ConfigChannel.burst_topicwho ? chptr->topic->topic_info : "",
|
|
ConfigChannel.burst_topicwho ? " " : "", chptr->topic->topic);
|
|
|
|
hchaninfo.chptr = chptr;
|
|
call_hook(h_burst_channel, &hchaninfo);
|
|
}
|
|
|
|
hclientinfo.target = NULL;
|
|
call_hook(h_burst_finished, &hclientinfo);
|
|
}
|
|
|
|
/*
|
|
* server_estab
|
|
*
|
|
* inputs - pointer to a struct Client
|
|
* output -
|
|
* side effects -
|
|
*/
|
|
static int
|
|
server_estab(struct Client *client_p)
|
|
{
|
|
struct Client *target_p;
|
|
struct server_conf *server_p;
|
|
hook_data_client hdata;
|
|
const char *host;
|
|
char note[HOSTLEN + 10];
|
|
rb_dlink_node *ptr;
|
|
|
|
s_assert(NULL != client_p);
|
|
if(client_p == NULL)
|
|
return -1;
|
|
|
|
host = client_p->name;
|
|
|
|
if((server_p = client_p->localClient->att_sconf) == NULL)
|
|
{
|
|
/* This shouldn't happen, better tell the ops... -A1kmm */
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Warning: Lost connect{} block for server %s!", host);
|
|
ilog(L_SERVER, "Lost connect{} block for server %s", host);
|
|
return exit_client(client_p, client_p, client_p, "Lost connect{} block!");
|
|
}
|
|
|
|
/* We shouldn't have to check this, it should already done before
|
|
* server_estab is called. -A1kmm
|
|
*/
|
|
if(client_p->localClient->passwd)
|
|
{
|
|
memset(client_p->localClient->passwd, 0, strlen(client_p->localClient->passwd));
|
|
rb_free(client_p->localClient->passwd);
|
|
client_p->localClient->passwd = NULL;
|
|
}
|
|
|
|
/* Its got identd , since its a server */
|
|
SetGotId(client_p);
|
|
|
|
/* If there is something in the serv_list, it might be this
|
|
* connecting server..
|
|
*/
|
|
if(!ServerInfo.hub && serv_list.head)
|
|
{
|
|
if(client_p != serv_list.head->data || serv_list.head->next)
|
|
{
|
|
ServerStats.is_ref++;
|
|
sendto_one(client_p, "ERROR :I'm a leaf not a hub");
|
|
return exit_client(client_p, client_p, client_p, "I'm a leaf");
|
|
}
|
|
}
|
|
|
|
if(IsUnknown(client_p))
|
|
{
|
|
/*
|
|
* jdc -- 1. Use EmptyString(), not [0] index reference.
|
|
* 2. Check ->spasswd, not ->passwd.
|
|
*/
|
|
if(!EmptyString(server_p->spasswd))
|
|
{
|
|
sendto_one(client_p, "PASS %s TS %d :%s",
|
|
server_p->spasswd, TS_CURRENT, me.id);
|
|
}
|
|
|
|
/* pass info to new server */
|
|
send_capabilities(client_p, default_server_capabs
|
|
| (ServerConfCompressed(server_p) && zlib_ok ? CAP_ZIP : 0)
|
|
| (ServerConfTb(server_p) ? CAP_TB : 0));
|
|
|
|
sendto_one(client_p, "SERVER %s 1 :%s%s",
|
|
me.name,
|
|
ConfigServerHide.hidden ? "(H) " : "",
|
|
(me.info[0]) ? (me.info) : "IRCers United");
|
|
}
|
|
|
|
if(!rb_set_buffers(client_p->localClient->F, READBUF_SIZE))
|
|
report_error("rb_set_buffers failed for server %s:%s",
|
|
client_p->name, log_client_name(client_p, SHOW_IP), errno);
|
|
|
|
/* Buffer up the burst before trying to send anything.
|
|
* In any case, this saves on system calls, and for ziplinks it
|
|
* is required so that we only start sending it when ssld confirms
|
|
* it has enabled compression ('R' message on control pipe).
|
|
*/
|
|
SetCork(client_p);
|
|
|
|
/* Enable compression now */
|
|
if(IsCapable(client_p, CAP_ZIP))
|
|
{
|
|
start_zlib_session(client_p);
|
|
}
|
|
sendto_one(client_p, "SVINFO %d %d 0 :%ld", TS_CURRENT, TS_MIN,
|
|
(long int)rb_current_time());
|
|
|
|
client_p->servptr = &me;
|
|
|
|
if(IsAnyDead(client_p))
|
|
return CLIENT_EXITED;
|
|
|
|
SetServer(client_p);
|
|
|
|
/* Update the capability combination usage counts */
|
|
set_chcap_usage_counts(client_p);
|
|
|
|
rb_dlinkAdd(client_p, &client_p->lnode, &me.serv->servers);
|
|
rb_dlinkMoveNode(&client_p->localClient->tnode, &unknown_list, &serv_list);
|
|
rb_dlinkAddTailAlloc(client_p, &global_serv_list);
|
|
|
|
if(has_id(client_p))
|
|
add_to_hash(HASH_ID, client_p->id, client_p);
|
|
|
|
add_to_hash(HASH_CLIENT, client_p->name, client_p);
|
|
/* doesnt duplicate client_p->serv if allocated this struct already */
|
|
make_server(client_p);
|
|
|
|
client_p->serv->caps = client_p->localClient->caps;
|
|
|
|
if(client_p->localClient->fullcaps)
|
|
{
|
|
client_p->serv->fullcaps = rb_strdup(client_p->localClient->fullcaps);
|
|
rb_free(client_p->localClient->fullcaps);
|
|
client_p->localClient->fullcaps = NULL;
|
|
}
|
|
|
|
/* add it to scache */
|
|
scache_add(client_p->name);
|
|
client_p->localClient->firsttime = rb_current_time();
|
|
/* fixing eob timings.. -gnp */
|
|
|
|
/* Show the real host/IP to admins */
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link with %s established: (%s) link",
|
|
client_p->name, show_capabilities(client_p));
|
|
|
|
ilog(L_SERVER, "Link with %s established: (%s) link",
|
|
log_client_name(client_p, SHOW_IP), show_capabilities(client_p));
|
|
|
|
if(IsCapable(client_p, CAP_SAVE) && !IsCapable(client_p, CAP_SAVETS_100))
|
|
{
|
|
sendto_realops_flags(UMODE_ALL, L_ALL,
|
|
"Link %s SAVE protocol mismatch. Users timestamps may be desynced after SAVE",
|
|
client_p->name);
|
|
ilog(L_SERVER,
|
|
"Link %s SAVE protocol mismatch. Users timestamps may be desynced after SAVE",
|
|
log_client_name(client_p, SHOW_IP));
|
|
}
|
|
|
|
hdata.client = &me;
|
|
hdata.target = client_p;
|
|
call_hook(h_server_introduced, &hdata);
|
|
|
|
rb_snprintf(note, sizeof(note), "Server: %s", client_p->name);
|
|
rb_note(client_p->localClient->F, note);
|
|
|
|
/*
|
|
** Old sendto_serv_but_one() call removed because we now
|
|
** need to send different names to different servers
|
|
** (domain name matching) Send new server to other servers.
|
|
*/
|
|
RB_DLINK_FOREACH(ptr, serv_list.head)
|
|
{
|
|
target_p = ptr->data;
|
|
|
|
if(target_p == client_p)
|
|
continue;
|
|
|
|
if(has_id(target_p) && has_id(client_p))
|
|
{
|
|
sendto_one(target_p, ":%s SID %s 2 %s :%s%s",
|
|
me.id, client_p->name, client_p->id,
|
|
IsHidden(client_p) ? "(H) " : "", client_p->info);
|
|
|
|
if(IsCapable(target_p, CAP_ENCAP) && !EmptyString(client_p->serv->fullcaps))
|
|
sendto_one(target_p, ":%s ENCAP * GCAP :%s",
|
|
client_p->id, client_p->serv->fullcaps);
|
|
}
|
|
else
|
|
{
|
|
sendto_one(target_p, ":%s SERVER %s 2 :%s%s",
|
|
me.name, client_p->name,
|
|
IsHidden(client_p) ? "(H) " : "", client_p->info);
|
|
|
|
if(IsCapable(target_p, CAP_ENCAP) && !EmptyString(client_p->serv->fullcaps))
|
|
sendto_one(target_p, ":%s ENCAP * GCAP :%s",
|
|
client_p->name, client_p->serv->fullcaps);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Pass on my client information to the new server
|
|
**
|
|
** First, pass only servers (idea is that if the link gets
|
|
** cancelled beacause the server was already there,
|
|
** there are no NICK's to be cancelled...). Of course,
|
|
** if cancellation occurs, all this info is sent anyway,
|
|
** and I guess the link dies when a read is attempted...? --msa
|
|
**
|
|
** Note: Link cancellation to occur at this point means
|
|
** that at least two servers from my fragment are building
|
|
** up connection this other fragment at the same time, it's
|
|
** a race condition, not the normal way of operation...
|
|
**
|
|
** ALSO NOTE: using the get_client_name for server names--
|
|
** see previous *WARNING*!!! (Also, original inpath
|
|
** is destroyed...)
|
|
*/
|
|
RB_DLINK_FOREACH(ptr, global_serv_list.head)
|
|
{
|
|
target_p = ptr->data;
|
|
|
|
/* target_p->from == target_p for target_p == client_p */
|
|
if(IsMe(target_p) || target_p->from == client_p)
|
|
continue;
|
|
|
|
/* presumption, if target has an id, so does its uplink */
|
|
if(has_id(client_p) && has_id(target_p))
|
|
sendto_one(client_p, ":%s SID %s %d %s :%s%s",
|
|
target_p->servptr->id, target_p->name,
|
|
target_p->hopcount + 1, target_p->id,
|
|
IsHidden(target_p) ? "(H) " : "", target_p->info);
|
|
else
|
|
sendto_one(client_p, ":%s SERVER %s %d :%s%s",
|
|
target_p->servptr->name,
|
|
target_p->name, target_p->hopcount + 1,
|
|
IsHidden(target_p) ? "(H) " : "", target_p->info);
|
|
|
|
if(IsCapable(client_p, CAP_ENCAP) && !EmptyString(target_p->serv->fullcaps))
|
|
sendto_one(client_p, ":%s ENCAP * GCAP :%s",
|
|
get_id(target_p, client_p), target_p->serv->fullcaps);
|
|
}
|
|
|
|
if(has_id(client_p))
|
|
burst_TS6(client_p);
|
|
else
|
|
burst_TS5(client_p);
|
|
|
|
/* Always send a PING after connect burst is done */
|
|
sendto_one(client_p, "PING :%s", get_id(&me, client_p));
|
|
|
|
ClearCork(client_p);
|
|
send_pop_queue(client_p);
|
|
return 0;
|
|
}
|