mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-02 00:26:37 +02:00
PROTOCTL EAUTH/SERVERS/new linking protocol:
- Server protocol: added PROTOCTL EATH=servername, which allows us to
authenticate the server very early in the handshake process. That way,
certain commands and PROTOCTL tokens can 'trust' the server.
See doc/technical/protoctl.txt for details.
- Server protocol: between new Unreal servers we now do the handshake a
little bit different, so it waits with sending the SERVER command until
the first PROTOCTL is received. Needed for next.
- Server protocol: added PROTOCTL SERVERS=1,2,3,4,etc by which a server can
inform the other server which servers (server numeric, actually) it has
linked. See doc/technical/protoctl.txt and next for details.
- When our server was trying to link to some server, and at the same time
another server was also trying to link with us, this would lead to a
server collision: the server would link (twice) ok at first, but then a
second later or so both would quit with 'Server Exists' with quite some
mess as a result. This isn't unique to Unreal, btw.
This happened more often when you had a low connfreq in your link blocks
(aka: quick reconnects), or had multiple hubs on autoconnect (with same
connfreq), or when you (re)started all servers at the same time.
This should now be solved by a new server handshake design, which detects
this race condition and solves it by closing one of the two (or more)
connections to avoid the issue.
This also means that it should now be safe to have multiple hubs with low
connfreq's (eg: 10s) without risking that your network falls apart.
This new server handshake (protocol updates, etc) was actually quite some
work, especially for something that only happened sporadically. I felt it
was needed though, because (re)linking stability is extremely important.
This new feature/design/fix requires extensive testing.
This feature can be disabled by: set { new-linking-protocol 0; };
This commit is contained in:
@@ -1861,3 +1861,31 @@
|
||||
- Use RPL_STARTTLS/ERR_STARTTLS numerics
|
||||
- Removed log target 'kline' from documentation, as it didn't do anything
|
||||
(use 'tkl' instead). Reported by nephilim and Stealth (#0003849).
|
||||
- Server protocol: added PROTOCTL EATH=servername, which allows us to
|
||||
authenticate the server very early in the handshake process. That way,
|
||||
certain commands and PROTOCTL tokens can 'trust' the server.
|
||||
See doc/technical/protoctl.txt for details.
|
||||
- Server protocol: between new Unreal servers we now do the handshake a
|
||||
little bit different, so it waits with sending the SERVER command until
|
||||
the first PROTOCTL is received. Needed for next.
|
||||
- Server protocol: added PROTOCTL SERVERS=1,2,3,4,etc by which a server can
|
||||
inform the other server which servers (server numeric, actually) it has
|
||||
linked. See doc/technical/protoctl.txt and next for details.
|
||||
- When our server was trying to link to some server, and at the same time
|
||||
another server was also trying to link with us, this would lead to a
|
||||
server collision: the server would link (twice) ok at first, but then a
|
||||
second later or so both would quit with 'Server Exists' with quite some
|
||||
mess as a result. This isn't unique to Unreal, btw.
|
||||
This happened more often when you had a low connfreq in your link blocks
|
||||
(aka: quick reconnects), or had multiple hubs on autoconnect (with same
|
||||
connfreq), or when you (re)started all servers at the same time.
|
||||
This should now be solved by a new server handshake design, which detects
|
||||
this race condition and solves it by closing one of the two (or more)
|
||||
connections to avoid the issue.
|
||||
This also means that it should now be safe to have multiple hubs with low
|
||||
connfreq's (eg: 10s) without risking that your network falls apart.
|
||||
This new server handshake (protocol updates, etc) was actually quite some
|
||||
work, especially for something that only happened sporadically. I felt it
|
||||
was needed though, because (re)linking stability is extremely important.
|
||||
This new feature/design/fix requires extensive testing.
|
||||
This feature can be disabled by: set { new-linking-protocol 0; };
|
||||
|
||||
@@ -139,3 +139,18 @@ NICKCHARS This specifies a list of language characters that are allowed in n
|
||||
The items in the list sent as NICKCHARS=.. must always be sorted.
|
||||
If a server sends NICKCHARS= and if the remote parameters do not match the
|
||||
charsets in use locally, then the server link is rejected.
|
||||
|
||||
CHANMODES Like CHANMODES from the 005 numeric. Useful to see which channel modes are
|
||||
supported/used, and can also be used to properly eat parameters in parameter
|
||||
modes in the MODE command (for eg: +jk 1:1 a).
|
||||
|
||||
EAUTH Early Authorization. This makes it possible for servers to authenticate each
|
||||
other before the regular SERVER command. Needs to be done prior to using the
|
||||
SERVERS token, and possibly other tokens or commands in the future. Hence,
|
||||
is recommended to be sent as first (or early) PROTOCTL token. Note also that
|
||||
the PASS command should be sent prior to this PROTOCTL token.
|
||||
EAUTH=my.server.name[,options]
|
||||
|
||||
SERVERS Informs the other server about the other servers (numerics) on this network
|
||||
(including our own numeric).
|
||||
Syntax: SERVERS=numeric1,numeric2,numeric3,etc
|
||||
|
||||
@@ -125,6 +125,7 @@ struct zConfiguration {
|
||||
char *restrict_usermodes;
|
||||
char *restrict_channelmodes;
|
||||
char *restrict_extendedbans;
|
||||
int new_linking_protocol;
|
||||
char *channel_command_prefix;
|
||||
long unknown_flood_bantime;
|
||||
long unknown_flood_amount;
|
||||
@@ -222,6 +223,7 @@ extern MODVAR aConfiguration iConf;
|
||||
#define RESTRICT_USERMODES iConf.restrict_usermodes
|
||||
#define RESTRICT_CHANNELMODES iConf.restrict_channelmodes
|
||||
#define RESTRICT_EXTENDEDBANS iConf.restrict_extendedbans
|
||||
#define NEW_LINKING_PROTOCOL iConf.new_linking_protocol
|
||||
#ifdef THROTTLING
|
||||
#define THROTTLING_PERIOD iConf.throttle_period
|
||||
#define THROTTLING_COUNT iConf.throttle_count
|
||||
@@ -333,6 +335,7 @@ struct SetCheck {
|
||||
unsigned has_restrict_usermodes:1;
|
||||
unsigned has_restrict_channelmodes:1;
|
||||
unsigned has_restrict_extendedbans:1;
|
||||
unsigned has_new_linking_protocol:1;
|
||||
unsigned has_channel_command_prefix:1;
|
||||
unsigned has_anti_flood_unknown_flood_bantime:1;
|
||||
unsigned has_anti_flood_unknown_flood_amount:1;
|
||||
|
||||
@@ -547,6 +547,7 @@ extern int Auth_CheckError(ConfigEntry *ce);
|
||||
extern long xbase64dec(char *b64);
|
||||
extern aClient *find_server_b64_or_real(char *name);
|
||||
extern aClient *find_server_by_base64(char *b64);
|
||||
extern aClient *find_server_by_numeric(long value);
|
||||
extern int is_chanownprotop(aClient *cptr, aChannel *chptr);
|
||||
extern int is_skochanop(aClient *cptr, aChannel *chptr);
|
||||
extern char *make_virthost(aClient *sptr, char *curr, char *new, int mode);
|
||||
@@ -747,6 +748,8 @@ extern MODVAR unsigned char *(*StripColors)(unsigned char *text);
|
||||
extern MODVAR const char *(*StripControlCodes)(unsigned char *text);
|
||||
extern MODVAR void (*spamfilter_build_user_string)(char *buf, char *nick, aClient *acptr);
|
||||
extern MODVAR int (*is_silenced)(aClient *sptr, aClient *acptr);
|
||||
extern MODVAR void (*send_protoctl_servers)(aClient *sptr, int response);
|
||||
extern MODVAR int (*verify_link)(aClient *cptr, aClient *sptr, char *servername, ConfigItem_link **link_out);
|
||||
/* /Efuncs */
|
||||
extern MODVAR aMotd *opermotd, *svsmotd, *motd, *botmotd, *smotd;
|
||||
extern MODVAR int max_connection_count;
|
||||
@@ -795,3 +798,8 @@ extern void free_motd(aMotd *m);
|
||||
extern void fix_timers(void);
|
||||
extern char *chfl_to_sjoin_symbol(int s);
|
||||
extern char chfl_to_chanmode(int s);
|
||||
extern void add_pending_net(aClient *sptr, char *str);
|
||||
extern void free_pending_net(aClient *sptr);
|
||||
extern aPendingNet *find_pending_net_by_numeric_butone(int numeric, aClient *exempt);
|
||||
extern aClient *find_pending_net_duplicates(aClient *cptr, aClient **srv, int *numeric);
|
||||
extern aClient *find_non_pending_net_duplicates(aClient *cptr);
|
||||
|
||||
@@ -700,6 +700,8 @@ int CallCmdoverride(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, ch
|
||||
#define EFUNC_STRIPCONTROLCODES 32
|
||||
#define EFUNC_SPAMFILTER_BUILD_USER_STRING 33
|
||||
#define EFUNC_IS_SILENCED 34
|
||||
#define EFUNC_SEND_PROTOCTL_SERVERS 35
|
||||
#define EFUNC_VERIFY_LINK 36
|
||||
|
||||
/* Module flags */
|
||||
#define MODFLAG_NONE 0x0000
|
||||
|
||||
+11
-2
@@ -151,6 +151,7 @@ typedef struct SMember Member;
|
||||
typedef struct SMembership Membership;
|
||||
typedef struct SMembershipL MembershipL;
|
||||
typedef struct JFlood aJFlood;
|
||||
typedef struct PendingNet aPendingNet;
|
||||
|
||||
#ifdef ZIP_LINKS
|
||||
typedef struct Zdata aZdata;
|
||||
@@ -317,7 +318,7 @@ typedef unsigned int u_int32_t; /* XXX Hope this works! */
|
||||
#define FLAGS_SQUIT 0x20000 /* Server has been /squit by an oper */
|
||||
#define FLAGS_PROTOCTL 0x40000 /* Received a PROTOCTL message */
|
||||
#define FLAGS_PING 0x80000
|
||||
#define FLAGS_ASKEDPING 0x100000
|
||||
#define FLAGS_EAUTH 0x100000
|
||||
#define FLAGS_NETINFO 0x200000
|
||||
#define FLAGS_HYBNOTICE 0x400000
|
||||
#define FLAGS_QUARANTINE 0x800000
|
||||
@@ -412,7 +413,8 @@ typedef unsigned int u_int32_t; /* XXX Hope this works! */
|
||||
#define GotNetInfo(x) ((x)->flags & FLAGS_NETINFO)
|
||||
#define SetNetInfo(x) ((x)->flags |= FLAGS_NETINFO)
|
||||
#define IsCGIIRC(x) ((x)->flags & FLAGS_CGIIRC)
|
||||
|
||||
#define SetEAuth(x) ((x)->flags |= FLAGS_EAUTH)
|
||||
#define IsEAuth(x) ((x)->flags & FLAGS_EAUTH)
|
||||
#define IsShunned(x) ((x)->flags & FLAGS_SHUNNED)
|
||||
#define SetShunned(x) ((x)->flags |= FLAGS_SHUNNED)
|
||||
#define ClearShunned(x) ((x)->flags &= ~FLAGS_SHUNNED)
|
||||
@@ -1855,6 +1857,13 @@ struct JFlood {
|
||||
};
|
||||
#endif
|
||||
|
||||
struct PendingNet {
|
||||
aPendingNet *prev, *next; /* Previous and next in list */
|
||||
aClient *sptr; /**< Client to which these servers belong */
|
||||
int numservers; /**< Amount of servers in list */
|
||||
int servers[1]; /** The list of servers (array of integer server numerics) */
|
||||
};
|
||||
|
||||
void init_throttling_hash();
|
||||
int hash_throttling(struct IN_ADDR *in);
|
||||
struct ThrottlingBucket *find_throttling_bucket(struct IN_ADDR *in);
|
||||
|
||||
+5
-1
@@ -116,6 +116,8 @@ unsigned char *(*StripColors)(unsigned char *text);
|
||||
const char *(*StripControlCodes)(unsigned char *text);
|
||||
void (*spamfilter_build_user_string)(char *buf, char *nick, aClient *acptr);
|
||||
int (*is_silenced)(aClient *sptr, aClient *acptr);
|
||||
void (*send_protoctl_servers)(aClient *sptr, int response);
|
||||
int (*verify_link)(aClient *cptr, aClient *sptr, char *servername, ConfigItem_link **link_out);
|
||||
|
||||
static const EfunctionsList efunction_table[MAXEFUNCTIONS] = {
|
||||
/* 00 */ {NULL, NULL},
|
||||
@@ -153,7 +155,9 @@ static const EfunctionsList efunction_table[MAXEFUNCTIONS] = {
|
||||
/* 32 */ {"StripControlCodes", (void *)&StripControlCodes},
|
||||
/* 33 */ {"spamfilter_build_user_string", (void *)&spamfilter_build_user_string},
|
||||
/* 34 */ {"is_silenced", (void *)&is_silenced},
|
||||
/* 35 */ {NULL, NULL}
|
||||
/* 35 */ {"send_protoctl_servers", (void *)&send_protoctl_servers},
|
||||
/* 36 */ {"verify_link", (void *)&verify_link},
|
||||
/* 37 */ {NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ ModuleHeader l_commands_Header
|
||||
extern int m_htm_Test(ModuleInfo *modinfo), m_join_Test(ModuleInfo *modinfo);
|
||||
extern int m_mode_Test(ModuleInfo *modinfo), m_nick_Test(ModuleInfo *modinfo);
|
||||
extern int m_tkl_Test(ModuleInfo *modinfo), m_list_Test(ModuleInfo *modinfo);
|
||||
extern int m_message_Test(ModuleInfo *modinfo);
|
||||
extern int m_message_Test(ModuleInfo *modinfo), m_server_Test(ModuleInfo *modinfo);
|
||||
|
||||
extern int m_sethost_Init(ModuleInfo *modinfo), m_setname_Init(ModuleInfo *modinfo), m_chghost_Init(ModuleInfo *modinfo);
|
||||
extern int m_chgident_Init(ModuleInfo *modinfo), m_setident_Init(ModuleInfo *modinfo), m_sdesc_Init(ModuleInfo *modinfo);
|
||||
@@ -228,6 +228,7 @@ int l_commands_Test(ModuleInfo *modinfo)
|
||||
m_tkl_Test(ModCmdsInfo);
|
||||
m_list_Test(ModCmdsInfo);
|
||||
m_message_Test(ModCmdsInfo);
|
||||
m_server_Test(ModCmdsInfo);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
+103
-3
@@ -40,12 +40,12 @@
|
||||
#ifdef STRIPBADWORDS
|
||||
#include "badwords.h"
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include "version.h"
|
||||
#endif
|
||||
|
||||
DLLFUNC int m_protoctl(aClient *cptr, aClient *sptr, int parc, char *parv[]);
|
||||
|
||||
extern MODVAR char serveropts[];
|
||||
|
||||
#define MSG_PROTOCTL "PROTOCTL"
|
||||
#define TOK_PROTOCTL "_"
|
||||
|
||||
@@ -91,9 +91,13 @@ CMD_FUNC(m_protoctl)
|
||||
#ifndef PROTOCTL_MADNESS
|
||||
int remove = 0;
|
||||
#endif
|
||||
int first_protoctl = (GotProtoctl(sptr)) ? 0 : 1; /**< First PROTOCTL we receive? Special ;) */
|
||||
char proto[128], *s;
|
||||
/* static char *dummyblank = ""; Yes, it is kind of ugly */
|
||||
|
||||
if (!MyConnect(sptr))
|
||||
return 0; /* Remote PROTOCTL's are not supported at this time */
|
||||
|
||||
#ifdef PROTOCTL_MADNESS
|
||||
if (GotProtoctl(sptr))
|
||||
{
|
||||
@@ -334,7 +338,11 @@ CMD_FUNC(m_protoctl)
|
||||
}
|
||||
else if (strncmp(s, "NICKCHARS=", 10) == 0)
|
||||
{
|
||||
/* Compare... */
|
||||
if (!IsServer(cptr) && !IsEAuth(cptr) && !IsHandshake(cptr))
|
||||
continue;
|
||||
/* Ok, server is either authenticated, or is an outgoing connect...
|
||||
* We now compare the character sets to see if we should warn opers about any mismatch...
|
||||
*/
|
||||
if (strcmp(s+10, langsinuse))
|
||||
{
|
||||
sendto_realops("\002WARNING!!!!\002 Link %s does not have the same set::allowed-nickchars settings (or is "
|
||||
@@ -343,6 +351,87 @@ CMD_FUNC(m_protoctl)
|
||||
/* return exit_client(cptr, cptr, &me, "Nick charset mismatch"); */
|
||||
}
|
||||
}
|
||||
else if ((strncmp(s, "EAUTH=", 6) == 0) && NEW_LINKING_PROTOCOL)
|
||||
{
|
||||
/* Early authorization: EAUTH=servername[,options] */
|
||||
int ret;
|
||||
char *servername = s+6, *p;
|
||||
ConfigItem_link *aconf = NULL;
|
||||
|
||||
if (strlen(servername) > HOSTLEN)
|
||||
servername[HOSTLEN] = '\0';
|
||||
|
||||
for (p = servername; *p; *p++)
|
||||
{
|
||||
if (*p == ',')
|
||||
{
|
||||
/* Upwards compatible, if we ever add any options through EAUTH=blah,options */
|
||||
*p = '\0';
|
||||
break;
|
||||
}
|
||||
if (*p <= ' ' || *p > '~')
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p || !index(servername, '.'))
|
||||
{
|
||||
sendto_one(sptr, "ERROR :Bogus server name in EAUTH (%s)", servername);
|
||||
sendto_snomask
|
||||
(SNO_JUNK,
|
||||
"WARNING: Bogus server name (%s) from %s in EAUTH (maybe just a fishy client)",
|
||||
servername, get_client_name(cptr, TRUE));
|
||||
|
||||
return exit_client(cptr, sptr, &me, "Bogus server name");
|
||||
}
|
||||
|
||||
ret = verify_link(cptr, sptr, s+6, &aconf);
|
||||
if (ret < 0)
|
||||
return ret; /* FLUSH_BUFFER */
|
||||
|
||||
SetEAuth(cptr);
|
||||
if (!IsHandshake(cptr) && aconf && !BadPtr(aconf->connpwd)) /* Send PASS early... */
|
||||
sendto_one(sptr, "PASS :%s", aconf->connpwd);
|
||||
}
|
||||
else if ((strncmp(s, "SERVERS=", 8) == 0) && NEW_LINKING_PROTOCOL)
|
||||
{
|
||||
aClient *acptr, *srv;
|
||||
int numeric;
|
||||
|
||||
if (!IsEAuth(cptr))
|
||||
continue;
|
||||
|
||||
/* Other side lets us know which servers are behind it.
|
||||
* SERVERS=<numeric-of-server-1>[,<numeric-of-server-2[,..etc..]]
|
||||
* Eg: SERVER=1,2,3,4,5
|
||||
*/
|
||||
|
||||
add_pending_net(sptr, s+8);
|
||||
|
||||
acptr = find_non_pending_net_duplicates(sptr);
|
||||
if (acptr)
|
||||
{
|
||||
sendto_one(sptr, "ERROR :Server with numeric %d (%s) already exists",
|
||||
acptr->serv->numeric, acptr->name);
|
||||
sendto_realops("Link %s cancelled, server with numeric %d (%s) already exists",
|
||||
get_client_name(acptr, TRUE), acptr->serv->numeric, acptr->name);
|
||||
return exit_client(sptr, sptr, sptr, "Server Exists (or identical numeric)");
|
||||
}
|
||||
|
||||
acptr = find_pending_net_duplicates(sptr, &srv, &numeric);
|
||||
if (acptr)
|
||||
{
|
||||
sendto_one(sptr, "ERROR :Server with numeric %d is being introduced by another server as well. "
|
||||
"Just wait a moment for it to synchronize...", numeric);
|
||||
sendto_realops("Link %s cancelled, server would introduce server with numeric %d, which "
|
||||
"server %s is also about to introduce. Just wait a moment for it to synchronize...",
|
||||
get_client_name(acptr, TRUE), numeric, get_client_name(srv, TRUE));
|
||||
return exit_client(sptr, sptr, sptr, "Server Exists (just wait a moment)");
|
||||
}
|
||||
|
||||
/* Send our PROTOCTL SERVERS= back if this was NOT a response */
|
||||
if (s[8] != '*')
|
||||
send_protoctl_servers(sptr, 1);
|
||||
}
|
||||
/*
|
||||
* Add other protocol extensions here, with proto
|
||||
* containing the base option, and options containing
|
||||
@@ -353,5 +442,16 @@ CMD_FUNC(m_protoctl)
|
||||
*/
|
||||
}
|
||||
|
||||
if (first_protoctl && IsHandshake(cptr) && sptr->serv) /* first & outgoing connection to server */
|
||||
{
|
||||
/* SERVER message moved from completed_connection() to here due to EAUTH/SERVERS PROTOCTL stuff,
|
||||
* which needed to be delayed until after both sides have received SERVERS=xx (..or not.. in case
|
||||
* of older servers).
|
||||
*/
|
||||
sendto_one(cptr, "SERVER %s 1 :U%d-%s%s-%i %s",
|
||||
me.name, UnrealProtocol, serveropts, extraflags ? extraflags : "", me.serv->numeric,
|
||||
me.info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+201
-122
@@ -46,6 +46,8 @@ void send_channel_modes(aClient *cptr, aChannel *chptr);
|
||||
void send_channel_modes_sjoin(aClient *cptr, aChannel *chptr);
|
||||
void send_channel_modes_sjoin3(aClient *cptr, aChannel *chptr);
|
||||
DLLFUNC int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[]);
|
||||
int _verify_link(aClient *cptr, aClient *sptr, char *servername, ConfigItem_link **link_out);
|
||||
void _send_protoctl_servers(aClient *sptr, int response);
|
||||
|
||||
static char buf[BUFSIZE];
|
||||
|
||||
@@ -62,6 +64,14 @@ ModuleHeader MOD_HEADER(m_server)
|
||||
NULL
|
||||
};
|
||||
|
||||
DLLFUNC int MOD_TEST(m_server)(ModuleInfo *modinfo)
|
||||
{
|
||||
MARK_AS_OFFICIAL_MODULE(modinfo);
|
||||
EfunctionAddVoid(modinfo->handle, EFUNC_SEND_PROTOCTL_SERVERS, _send_protoctl_servers);
|
||||
EfunctionAdd(modinfo->handle, EFUNC_VERIFY_LINK, _verify_link);
|
||||
return MOD_SUCCESS;
|
||||
}
|
||||
|
||||
DLLFUNC int MOD_INIT(m_server)(ModuleInfo *modinfo)
|
||||
{
|
||||
add_CommandX(MSG_SERVER, TOK_SERVER, m_server, MAXPARA, M_UNREGISTERED|M_SERVER);
|
||||
@@ -86,6 +96,190 @@ DLLFUNC int MOD_UNLOAD(m_server)(int module_unload)
|
||||
|
||||
int m_server_synch(aClient *cptr, long numeric, ConfigItem_link *conf);
|
||||
|
||||
/** Send our PROTOCTL SERVERS=x,x,x,x stuff.
|
||||
* When response is set, it will be PROTOCTL SERVERS=*x,x,x (mind the asterisk).
|
||||
*/
|
||||
void _send_protoctl_servers(aClient *sptr, int response)
|
||||
{
|
||||
Link *lp;
|
||||
char buf[512];
|
||||
|
||||
if (!NEW_LINKING_PROTOCOL)
|
||||
return;
|
||||
|
||||
ircsprintf(buf, "PROTOCTL EAUTH=%s SERVERS=%s",
|
||||
me.name, response ? "*" : "");
|
||||
|
||||
for (lp = Servers; lp; lp = lp->next)
|
||||
{
|
||||
int numeric = lp->value.cptr->serv->numeric;
|
||||
if (numeric <= 0)
|
||||
continue;
|
||||
ircsprintf(buf+strlen(buf),"%d,", numeric);
|
||||
if (strlen(buf) > sizeof(buf)-12)
|
||||
{
|
||||
/* This should only happen if you have like more than 120 servers.. that would be a tad extreme... */
|
||||
sendto_realops("send_protoctl_servers: Ehm.. you have a whole lot of servers linked, don't you?");
|
||||
break; /* prevent overflow */
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove final comma */
|
||||
if (buf[strlen(buf)-1] == ',')
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
|
||||
sendto_one(sptr, "%s", buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Verify server link.
|
||||
* This does authentication and authorization checks.
|
||||
* @param cptr The client directly connected to us (cptr).
|
||||
* @param sptr The client which (originally) issued the server command (sptr).
|
||||
* @param servername The server name provided by the client.
|
||||
* @param link_out Pointer-to-pointer-to-link block. Will be set when auth OK. Caller may pass NULL if he doesn't care.
|
||||
* @returns This function returns 0 on succesful auth, other values should be returned by
|
||||
* the calling function, as it will always be FLUSH_BUFFER due to exit_client().
|
||||
*/
|
||||
int _verify_link(aClient *cptr, aClient *sptr, char *servername, ConfigItem_link **link_out)
|
||||
{
|
||||
char xerrmsg[256];
|
||||
ConfigItem_link *link;
|
||||
char *inpath = get_client_name(cptr, TRUE);
|
||||
aClient *acptr = NULL, *ocptr = NULL;
|
||||
ConfigItem_ban *bconf;
|
||||
|
||||
if (link_out)
|
||||
*link_out = NULL;
|
||||
|
||||
strcpy(xerrmsg, "No matching link configuration");
|
||||
|
||||
if (!cptr->passwd)
|
||||
{
|
||||
sendto_one(cptr, "ERROR :Missing password");
|
||||
return exit_client(cptr, sptr, &me, "Missing password");
|
||||
}
|
||||
|
||||
|
||||
/* First check if the server is in the list */
|
||||
if (!servername) {
|
||||
strcpy(xerrmsg, "Null servername");
|
||||
goto errlink;
|
||||
}
|
||||
if (cptr->serv && cptr->serv->conf)
|
||||
{
|
||||
/* We already know what block we are dealing with (outgoing connect!) */
|
||||
link = cptr->serv->conf;
|
||||
} else {
|
||||
/* Hunt the linkblock down ;) */
|
||||
for(link = conf_link; link; link = (ConfigItem_link *) link->next)
|
||||
if (!match(link->servername, servername))
|
||||
break;
|
||||
}
|
||||
if (!link) {
|
||||
snprintf(xerrmsg, 256, "No link block named '%s'", servername);
|
||||
goto errlink;
|
||||
}
|
||||
if (link->username && match(link->username, cptr->username)) {
|
||||
snprintf(xerrmsg, 256, "Username '%s' didn't match '%s'",
|
||||
cptr->username, link->username);
|
||||
/* I assume nobody will have 2 link blocks with the same servername
|
||||
* and different username. -- Syzop
|
||||
*/
|
||||
goto errlink;
|
||||
}
|
||||
/* For now, we don't check based on DNS, it is slow, and IPs are better.
|
||||
* We also skip checking if link::options::nohostcheck is set.
|
||||
*/
|
||||
if (link->options & CONNECT_NOHOSTCHECK)
|
||||
goto nohostcheck;
|
||||
link = Find_link(cptr->username, cptr->sockhost, cptr->sockhost, servername);
|
||||
|
||||
#ifdef INET6
|
||||
/*
|
||||
* We first try match on uncompressed form ::ffff:192.168.1.5 thing included
|
||||
*/
|
||||
if (!link)
|
||||
link = Find_link(cptr->username, cptr->sockhost, Inet_ia2pNB(&cptr->ip, 0), servername);
|
||||
/*
|
||||
* Then on compressed
|
||||
*/
|
||||
if (!link)
|
||||
link = Find_link(cptr->username, cptr->sockhost, Inet_ia2pNB(&cptr->ip, 1), servername);
|
||||
#endif
|
||||
if (!link)
|
||||
{
|
||||
snprintf(xerrmsg, 256, "Server is in link block but IP/host didn't match");
|
||||
errlink:
|
||||
/* Send the "simple" error msg to the server */
|
||||
sendto_one(cptr,
|
||||
"ERROR :Link denied (No matching link configuration) %s",
|
||||
inpath);
|
||||
/* And send the "verbose" error msg only to local failops */
|
||||
sendto_locfailops
|
||||
("Link denied for %s(%s@%s) (%s) %s",
|
||||
servername, cptr->username, cptr->sockhost, xerrmsg, inpath);
|
||||
return exit_client(cptr, sptr, &me,
|
||||
"Link denied (No matching link configuration)");
|
||||
}
|
||||
nohostcheck:
|
||||
/* Now for checking passwords */
|
||||
if (Auth_Check(cptr, link->recvauth, cptr->passwd) == -1)
|
||||
{
|
||||
sendto_one(cptr,
|
||||
"ERROR :Link denied (Authentication failed) %s",
|
||||
inpath);
|
||||
sendto_locfailops
|
||||
("Link denied (Authentication failed [Bad password?]) %s", inpath);
|
||||
return exit_client(cptr, sptr, &me,
|
||||
"Link denied (Authentication failed)");
|
||||
}
|
||||
|
||||
/*
|
||||
* Third phase, we check that the server does not exist
|
||||
* already
|
||||
*/
|
||||
if ((acptr = find_server(servername, NULL)))
|
||||
{
|
||||
/* Found. Bad. Quit. */
|
||||
acptr = acptr->from;
|
||||
ocptr =
|
||||
(cptr->firsttime > acptr->firsttime) ? acptr : cptr;
|
||||
acptr =
|
||||
(cptr->firsttime > acptr->firsttime) ? cptr : acptr;
|
||||
sendto_one(acptr,
|
||||
"ERROR :Server %s already exists from %s",
|
||||
servername,
|
||||
(ocptr->from ? ocptr->from->name : "<nobody>"));
|
||||
sendto_realops
|
||||
("Link %s cancelled, server %s already exists from %s",
|
||||
get_client_name(acptr, TRUE), servername,
|
||||
(ocptr->from ? ocptr->from->name : "<nobody>"));
|
||||
return exit_client(acptr, acptr, acptr,
|
||||
"Server Exists");
|
||||
}
|
||||
if ((bconf = Find_ban(NULL, servername, CONF_BAN_SERVER)))
|
||||
{
|
||||
sendto_realops
|
||||
("Cancelling link %s, banned server",
|
||||
get_client_name(cptr, TRUE));
|
||||
sendto_one(cptr, "ERROR :Banned server (%s)", bconf->reason ? bconf->reason : "no reason");
|
||||
return exit_client(cptr, cptr, &me, "Banned server");
|
||||
}
|
||||
if (link->class->clients + 1 > link->class->maxclients)
|
||||
{
|
||||
sendto_realops
|
||||
("Cancelling link %s, full class",
|
||||
get_client_name(cptr, TRUE));
|
||||
return exit_client(cptr, cptr, &me, "Full class");
|
||||
}
|
||||
if (link_out)
|
||||
*link_out = link;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** m_server
|
||||
** parv[0] = sender prefix
|
||||
@@ -104,8 +298,6 @@ DLLFUNC CMD_FUNC(m_server)
|
||||
/* char *password = NULL; */
|
||||
char *ch = NULL; /* */
|
||||
char *inpath = get_client_name(cptr, TRUE);
|
||||
aClient *acptr = NULL, *ocptr = NULL;
|
||||
ConfigItem_ban *bconf;
|
||||
int hop = 0, numeric = 0;
|
||||
char info[REALLEN + 61];
|
||||
ConfigItem_link *aconf = NULL;
|
||||
@@ -177,125 +369,11 @@ DLLFUNC CMD_FUNC(m_server)
|
||||
if (IsUnknown(cptr) || IsHandshake(cptr))
|
||||
{
|
||||
char xerrmsg[256];
|
||||
ConfigItem_link *link;
|
||||
|
||||
strcpy(xerrmsg, "No matching link configuration");
|
||||
/* First check if the server is in the list */
|
||||
if (!servername) {
|
||||
strcpy(xerrmsg, "Null servername");
|
||||
goto errlink;
|
||||
}
|
||||
if (cptr->serv && cptr->serv->conf)
|
||||
{
|
||||
/* We already know what block we are dealing with (outgoing connect!) */
|
||||
link = cptr->serv->conf;
|
||||
} else {
|
||||
/* Hunt the linkblock down ;) */
|
||||
for(link = conf_link; link; link = (ConfigItem_link *) link->next)
|
||||
if (!match(link->servername, servername))
|
||||
break;
|
||||
}
|
||||
if (!link) {
|
||||
snprintf(xerrmsg, 256, "No link block named '%s'", servername);
|
||||
goto errlink;
|
||||
}
|
||||
if (link->username && match(link->username, cptr->username)) {
|
||||
snprintf(xerrmsg, 256, "Username '%s' didn't match '%s'",
|
||||
cptr->username, link->username);
|
||||
/* I assume nobody will have 2 link blocks with the same servername
|
||||
* and different username. -- Syzop
|
||||
*/
|
||||
goto errlink;
|
||||
}
|
||||
/* For now, we don't check based on DNS, it is slow, and IPs are better.
|
||||
* We also skip checking if link::options::nohostcheck is set.
|
||||
*/
|
||||
if (link->options & CONNECT_NOHOSTCHECK)
|
||||
{
|
||||
aconf = link;
|
||||
goto nohostcheck;
|
||||
}
|
||||
aconf = Find_link(cptr->username, cptr->sockhost, cptr->sockhost,
|
||||
servername);
|
||||
|
||||
#ifdef INET6
|
||||
/*
|
||||
* We first try match on uncompressed form ::ffff:192.168.1.5 thing included
|
||||
*/
|
||||
if (!aconf)
|
||||
aconf = Find_link(cptr->username, cptr->sockhost, Inet_ia2pNB(&cptr->ip, 0), servername);
|
||||
/*
|
||||
* Then on compressed
|
||||
*/
|
||||
if (!aconf)
|
||||
aconf = Find_link(cptr->username, cptr->sockhost, Inet_ia2pNB(&cptr->ip, 1), servername);
|
||||
#endif
|
||||
if (!aconf)
|
||||
{
|
||||
snprintf(xerrmsg, 256, "Server is in link block but IP/host didn't match");
|
||||
errlink:
|
||||
/* Send the "simple" error msg to the server */
|
||||
sendto_one(cptr,
|
||||
"ERROR :Link denied (No matching link configuration) %s",
|
||||
inpath);
|
||||
/* And send the "verbose" error msg only to local failops */
|
||||
sendto_locfailops
|
||||
("Link denied for %s(%s@%s) (%s) %s",
|
||||
servername, cptr->username, cptr->sockhost, xerrmsg, inpath);
|
||||
return exit_client(cptr, sptr, &me,
|
||||
"Link denied (No matching link configuration)");
|
||||
}
|
||||
nohostcheck:
|
||||
/* Now for checking passwords */
|
||||
if (Auth_Check(cptr, aconf->recvauth, cptr->passwd) == -1)
|
||||
{
|
||||
sendto_one(cptr,
|
||||
"ERROR :Link denied (Authentication failed) %s",
|
||||
inpath);
|
||||
sendto_locfailops
|
||||
("Link denied (Authentication failed [Bad password?]) %s", inpath);
|
||||
return exit_client(cptr, sptr, &me,
|
||||
"Link denied (Authentication failed)");
|
||||
}
|
||||
|
||||
/*
|
||||
* Third phase, we check that the server does not exist
|
||||
* already
|
||||
*/
|
||||
if ((acptr = find_server(servername, NULL)))
|
||||
{
|
||||
/* Found. Bad. Quit. */
|
||||
acptr = acptr->from;
|
||||
ocptr =
|
||||
(cptr->firsttime > acptr->firsttime) ? acptr : cptr;
|
||||
acptr =
|
||||
(cptr->firsttime > acptr->firsttime) ? cptr : acptr;
|
||||
sendto_one(acptr,
|
||||
"ERROR :Server %s already exists from %s",
|
||||
servername,
|
||||
(ocptr->from ? ocptr->from->name : "<nobody>"));
|
||||
sendto_realops
|
||||
("Link %s cancelled, server %s already exists from %s",
|
||||
get_client_name(acptr, TRUE), servername,
|
||||
(ocptr->from ? ocptr->from->name : "<nobody>"));
|
||||
return exit_client(acptr, acptr, acptr,
|
||||
"Server Exists");
|
||||
}
|
||||
if ((bconf = Find_ban(NULL, servername, CONF_BAN_SERVER)))
|
||||
{
|
||||
sendto_realops
|
||||
("Cancelling link %s, banned server",
|
||||
get_client_name(cptr, TRUE));
|
||||
sendto_one(cptr, "ERROR :Banned server (%s)", bconf->reason ? bconf->reason : "no reason");
|
||||
return exit_client(cptr, cptr, &me, "Banned server");
|
||||
}
|
||||
if (aconf->class->clients + 1 > aconf->class->maxclients)
|
||||
{
|
||||
sendto_realops
|
||||
("Cancelling link %s, full class",
|
||||
get_client_name(cptr, TRUE));
|
||||
return exit_client(cptr, cptr, &me, "Full class");
|
||||
}
|
||||
int ret;
|
||||
ret = verify_link(cptr, sptr, servername, &aconf);
|
||||
if (ret < 0)
|
||||
return ret; /* FLUSH_BUFFER / failure */
|
||||
|
||||
/* OK, let us check in the data now now */
|
||||
hop = TS2ts(parv[2]);
|
||||
numeric = (parc > 4) ? TS2ts(parv[3]) : 0;
|
||||
@@ -621,8 +699,8 @@ int m_server_synch(aClient *cptr, long numeric, ConfigItem_link *aconf)
|
||||
/* If this is an incomming connection, then we have just received
|
||||
* their stuff and now send our stuff back.
|
||||
*/
|
||||
send_proto(cptr, aconf);
|
||||
sendto_one(cptr, "PASS :%s", aconf->connpwd);
|
||||
send_proto(cptr, aconf);
|
||||
sendto_one(cptr, "SERVER %s 1 :U%d-%s-%i %s",
|
||||
me.name, UnrealProtocol,
|
||||
serveropts, me.serv->numeric,
|
||||
@@ -659,6 +737,7 @@ int m_server_synch(aClient *cptr, long numeric, ConfigItem_link *aconf)
|
||||
}
|
||||
#endif
|
||||
/* Set up server structure */
|
||||
free_pending_net(cptr);
|
||||
SetServer(cptr);
|
||||
IRCstats.me_servers++;
|
||||
IRCstats.servers++;
|
||||
|
||||
+7
-3
@@ -835,10 +835,14 @@ int completed_connection(aClient *cptr)
|
||||
if (!BadPtr(aconf->connpwd))
|
||||
sendto_one(cptr, "PASS :%s", aconf->connpwd);
|
||||
|
||||
send_protoctl_servers(cptr, 0);
|
||||
send_proto(cptr, aconf);
|
||||
sendto_one(cptr, "SERVER %s 1 :U%d-%s%s-%i %s",
|
||||
me.name, UnrealProtocol, serveropts, extraflags ? extraflags : "", me.serv->numeric,
|
||||
me.info);
|
||||
/* Sending SERVER message moved to m_protoctl, so it's send after the first PROTOCTL
|
||||
* we receive from the remote server. Of course, this assumes that the remote server
|
||||
* to which we are connecting will at least send one PROTOCTL... but since it's an
|
||||
* outgoing connect, we can safely assume it's a remote UnrealIRCd server (or some
|
||||
* other advanced server..). -- Syzop
|
||||
*/
|
||||
if (!IsDead(cptr))
|
||||
start_auth(cptr);
|
||||
|
||||
|
||||
@@ -1703,6 +1703,7 @@ void config_setdefaultsettings(aConfiguration *i)
|
||||
i->name_server = strdup("127.0.0.1"); /* default, especially needed for w2003+ in some rare cases */
|
||||
i->level_on_join = CHFL_CHANOP;
|
||||
i->watch_away_notification = 1;
|
||||
i->new_linking_protocol = 1;
|
||||
}
|
||||
|
||||
/* 1: needed for set::options::allow-part-if-shunned,
|
||||
@@ -7203,6 +7204,9 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce)
|
||||
else if (!strcmp(cep->ce_varname, "restrict-extendedbans")) {
|
||||
ircstrdup(tempiConf.restrict_extendedbans, cep->ce_vardata);
|
||||
}
|
||||
else if (!strcmp(cep->ce_varname, "new-linking-protocol")) {
|
||||
tempiConf.new_linking_protocol = atoi(cep->ce_vardata);
|
||||
}
|
||||
else if (!strcmp(cep->ce_varname, "anti-spam-quit-message-time")) {
|
||||
tempiConf.anti_spam_quit_message_time = config_checkval(cep->ce_vardata,CFG_TIME);
|
||||
}
|
||||
@@ -7897,6 +7901,11 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce)
|
||||
CheckDuplicate(cep, restrict_extendedbans, "restrict-extendedbans");
|
||||
CheckNull(cep);
|
||||
}
|
||||
else if (!strcmp(cep->ce_varname, "new-linking-protocol"))
|
||||
{
|
||||
CheckDuplicate(cep, new_linking_protocol, "new-linking-protocol");
|
||||
CheckNull(cep);
|
||||
}
|
||||
else if (!strcmp(cep->ce_varname, "dns")) {
|
||||
for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) {
|
||||
CheckNull(cepp);
|
||||
|
||||
+1
-1
@@ -538,7 +538,7 @@ int exit_client(aClient *cptr, aClient *sptr, aClient *from, char *comment)
|
||||
IRCstats.me_servers--;
|
||||
ircd_log(LOG_SERVER, "SQUIT %s (%s)", sptr->name, comment);
|
||||
}
|
||||
|
||||
free_pending_net(sptr);
|
||||
if (sptr->listener)
|
||||
if (sptr->listener->class && !IsOutgoing(sptr))
|
||||
{
|
||||
|
||||
+128
@@ -1276,3 +1276,131 @@ aClient *find_match_server(char *mask)
|
||||
}
|
||||
return acptr;
|
||||
}
|
||||
|
||||
aPendingNet *pendingnet = NULL;
|
||||
|
||||
void add_pending_net(aClient *sptr, char *str)
|
||||
{
|
||||
aPendingNet *e = NULL;
|
||||
int num = 1;
|
||||
char *p, *name;
|
||||
|
||||
if (BadPtr(str) || !sptr)
|
||||
return;
|
||||
|
||||
/* First, count them */
|
||||
for (p = str; *p; p++)
|
||||
if (*p == ',')
|
||||
num++;
|
||||
|
||||
/* Allocate */
|
||||
e = MyMallocEx(sizeof(aPendingNet) + (sizeof(int) * num));
|
||||
|
||||
e->numservers = num;
|
||||
e->sptr = sptr;
|
||||
|
||||
/* Fill in */
|
||||
num = 0;
|
||||
for (name = strtoken(&p, str, ","); name; name = strtoken(&p, NULL, ","))
|
||||
{
|
||||
if (!*name)
|
||||
continue;
|
||||
|
||||
/* skip any non-digit prefixes, if necessary. Possibly needed in the future. */
|
||||
while (*name && !isdigit(*name))
|
||||
name++;
|
||||
|
||||
e->servers[num++] = atoi(name);
|
||||
}
|
||||
|
||||
AddListItem(e, pendingnet);
|
||||
}
|
||||
|
||||
void free_pending_net(aClient *sptr)
|
||||
{
|
||||
aPendingNet *e, *e_next;
|
||||
|
||||
for (e = pendingnet; e; e = e_next)
|
||||
{
|
||||
e_next = e->next;
|
||||
if (e->sptr == sptr)
|
||||
{
|
||||
DelListItem(e, pendingnet);
|
||||
MyFree(e);
|
||||
/* Don't break, there can be multiple objects */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aPendingNet *find_pending_net_by_numeric_butone(int numeric, aClient *exempt)
|
||||
{
|
||||
aPendingNet *e;
|
||||
int i;
|
||||
|
||||
if (numeric <= 0)
|
||||
return NULL;
|
||||
|
||||
for (e = pendingnet; e; e = e->next)
|
||||
{
|
||||
if (e->sptr == exempt)
|
||||
continue;
|
||||
for (i = 0; i < e->numservers; i++)
|
||||
if (e->servers[i] == numeric)
|
||||
return e;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Search the pending connections list for any identical numerics */
|
||||
aClient *find_pending_net_duplicates(aClient *cptr, aClient **srv, int *numeric)
|
||||
{
|
||||
aPendingNet *e, *other;
|
||||
int i;
|
||||
|
||||
*srv = NULL;
|
||||
*numeric = 0;
|
||||
|
||||
for (e = pendingnet; e; e = e->next)
|
||||
{
|
||||
if (e->sptr != cptr)
|
||||
continue;
|
||||
/* Ok, found myself */
|
||||
for (i = 0; i < e->numservers; i++)
|
||||
{
|
||||
int curr_numeric = e->servers[i];
|
||||
other = find_pending_net_by_numeric_butone(curr_numeric, cptr);
|
||||
if (other)
|
||||
{
|
||||
*srv = e->sptr;
|
||||
*numeric = curr_numeric;
|
||||
return other->sptr; /* Found another (pending) server with identical numeric */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
aClient *find_non_pending_net_duplicates(aClient *cptr)
|
||||
{
|
||||
aPendingNet *e;
|
||||
int i;
|
||||
aClient *acptr;
|
||||
|
||||
for (e = pendingnet; e; e = e->next)
|
||||
{
|
||||
if (e->sptr != cptr)
|
||||
continue;
|
||||
/* Ok, found myself */
|
||||
for (i = 0; i < e->numservers; i++)
|
||||
{
|
||||
int numeric = e->servers[i];
|
||||
acptr = find_server_by_numeric(numeric);
|
||||
if (acptr)
|
||||
return acptr; /* Found another (fully CONNECTED) server with identical numeric */
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user