From 6c0ebb5bd36ee2feb4124864baccecd5f1dbf88c Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Fri, 10 Jul 2015 21:57:13 +0200 Subject: [PATCH] Protection against linking race conditions is back again (IOTW: allow very rapid re-linking), but only if your network is fully 3.4.x (actually: current git unreal34 or later) Re-implemented PROTOCTL SERVERS= which nenolod ripped out (#4355). Add 2nd argument to PROTOCTL EAUTH=servername,unrealprotocol Change UnrealProtocol from 2350 to 2351 --- doc/technical/vl.txt | 3 +- include/h.h | 4 +- include/struct.h | 16 ++++-- include/version.h | 2 +- src/modules/m_protoctl.c | 54 +++++++++++++++++- src/modules/m_server.c | 21 +++++-- src/s_serv.c | 116 +++++++++++++++++++++++---------------- 7 files changed, 153 insertions(+), 63 deletions(-) diff --git a/doc/technical/vl.txt b/doc/technical/vl.txt index 2c2f9e15b..c3d37d91c 100644 --- a/doc/technical/vl.txt +++ b/doc/technical/vl.txt @@ -2,7 +2,8 @@ VL Information (c) 2002 codemastr (Dominick Meglio) [codemastr@unrealircd.com] Protocol Version ------------------------------------------------------------------------------------------------ -2350 3.4-alpha* +2351 3.4-alpha5 +2350 3.4-alpha1/2/3/4 2311 3.2.10 2310 3.2.9 2309 3.2.6, 3.2.7, 3.2.8 diff --git a/include/h.h b/include/h.h index accdedb5b..713709fef 100644 --- a/include/h.h +++ b/include/h.h @@ -722,9 +722,9 @@ 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); +extern aPendingNet *find_pending_net_by_sid_butone(char *sid, aClient *exempt); +extern aClient *find_pending_net_duplicates(aClient *cptr, aClient **srv, char **sid); extern MODVAR char serveropts[]; extern MODVAR char *IsupportStrings[]; extern void finish_auth(aClient *acptr); diff --git a/include/struct.h b/include/struct.h index c383bbaa1..df4ad2c94 100644 --- a/include/struct.h +++ b/include/struct.h @@ -145,7 +145,6 @@ typedef struct _cmdoverride Cmdoverride; typedef struct SMember Member; typedef struct SMembership Membership; typedef struct SMembershipL MembershipL; -typedef struct PendingNet aPendingNet; #ifdef NEED_U_INT32_T typedef unsigned int u_int32_t; /* XXX Hope this works! */ @@ -706,6 +705,7 @@ struct Server { } flags; struct { char *chanmodes[4]; + int protocol; } features; }; @@ -1719,11 +1719,17 @@ struct _parsemode { char buf[512]; /* internal parse buffer */ }; +typedef struct PendingServer aPendingServer; +struct PendingServer { + aPendingServer *prev, *next; + char sid[IDLEN+1]; +}; + +typedef struct PendingNet aPendingNet; 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) */ + aPendingNet *prev, *next; /* Previous and next in list */ + aClient *sptr; /**< Client to which these servers belong */ + aPendingServer *servers; /**< The list of servers connected to the client */ }; void init_throttling_hash(); diff --git a/include/version.h b/include/version.h index 102139083..178d0c90c 100644 --- a/include/version.h +++ b/include/version.h @@ -56,7 +56,7 @@ */ #define UNREAL_VERSION_TIME 201411 -#define UnrealProtocol 2350 +#define UnrealProtocol 2351 #define PATCH1 macro_to_str(UNREAL_VERSION_GENERATION) #define PATCH2 "." macro_to_str(UNREAL_VERSION_MAJOR) #define PATCH3 "" diff --git a/src/modules/m_protoctl.c b/src/modules/m_protoctl.c index 4c0880db6..35cdeb3fe 100644 --- a/src/modules/m_protoctl.c +++ b/src/modules/m_protoctl.c @@ -316,6 +316,7 @@ CMD_FUNC(m_protoctl) { /* Early authorization: EAUTH=servername[,options] */ int ret; + int protocol = 0; char *servername = s+6, *p; ConfigItem_link *aconf = NULL; @@ -326,8 +327,15 @@ CMD_FUNC(m_protoctl) { if (*p == ',') { - /* Upwards compatible, if we ever add any options through EAUTH=blah,options */ + char *x; + *p = '\0'; + + /* upwards compatible */ + x = strchr(p+1, ','); + if (x) + *x = '\0'; + protocol = atoi(p+1); break; } if (*p <= ' ' || *p > '~') @@ -351,9 +359,53 @@ CMD_FUNC(m_protoctl) SetEAuth(cptr); make_server(cptr); /* allocate and set cptr->serv */ + cptr->serv->features.protocol = protocol; if (!IsHandshake(cptr) && aconf) /* Send PASS early... */ sendto_one(sptr, "PASS :%s", (aconf->auth->type == AUTHTYPE_PLAINTEXT) ? aconf->auth->data : "*"); } + else if ((strncmp(s, "SERVERS=", 8) == 0) && NEW_LINKING_PROTOCOL) + { + aClient *acptr, *srv; + char *sid = NULL; + + if (!IsEAuth(cptr)) + continue; + + if (cptr->serv->features.protocol < 2351) + continue; /* old SERVERS= version */ + + /* Other side lets us know which servers are behind it. + * SERVERS=[,id, acptr->name); + sendto_realops("Link %s cancelled, server with SID %s (%s) already exists", + get_client_name(acptr, TRUE), acptr->id, acptr->name); + return exit_client(sptr, sptr, sptr, "Server Exists (or non-unique me::sid)"); + } + + acptr = find_pending_net_duplicates(sptr, &srv, &sid); + if (acptr) + { + sendto_one(sptr, "ERROR :Server with SID %s is being introduced by another server as well. " + "Just wait a moment for it to synchronize...", sid); + sendto_realops("Link %s cancelled, server would introduce server with SID %s, which " + "server %s is also about to introduce. Just wait a moment for it to synchronize...", + get_client_name(acptr, TRUE), sid, 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); + } else if ((strncmp(s, "TS=",3) == 0) && (IsServer(sptr) || IsEAuth(sptr))) { long t = atol(s+3); diff --git a/src/modules/m_server.c b/src/modules/m_server.c index a7eb642ca..3cc4e0bb8 100644 --- a/src/modules/m_server.c +++ b/src/modules/m_server.c @@ -98,15 +98,26 @@ int m_server_synch(aClient *cptr, ConfigItem_link *conf); */ void _send_protoctl_servers(aClient *sptr, int response) { -Link *lp; -char buf[512]; + char buf[512]; + aClient *acptr; if (!NEW_LINKING_PROTOCOL) return; - // TODO: reintroduce SERVERS=... - ircsnprintf(buf, sizeof(buf), "PROTOCTL EAUTH=%s", - me.name); + ircsnprintf(buf, sizeof(buf), "PROTOCTL EAUTH=%s,%d SERVERS=%s", + me.name, UnrealProtocol, response ? "*" : ""); + + list_for_each_entry(acptr, &global_server_list, client_node) + { + if (*acptr->id) + snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s,", acptr->id); + if (strlen(buf) > sizeof(buf)-12) + break; /* prevent overflow/cutoff if you have a network with more than 90 servers or something. */ + } + + /* Remove final comma (if any) */ + if (buf[strlen(buf)-1] == ',') + buf[strlen(buf)-1] = '\0'; sendto_one(sptr, "%s", buf); } diff --git a/src/s_serv.c b/src/s_serv.c index d7fc1c20a..1769b3bda 100644 --- a/src/s_serv.c +++ b/src/s_serv.c @@ -1264,98 +1264,95 @@ aPendingNet *pendingnet = NULL; void add_pending_net(aClient *sptr, char *str) { -aPendingNet *e = NULL; -int num = 1; -char *p, *name; + aPendingNet *net; + aPendingServer *srv; + 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; + net = MyMallocEx(sizeof(aPendingNet)); + net->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); + + srv = MyMallocEx(sizeof(aPendingServer)); + strlcpy(srv->sid, name, sizeof(srv->sid)); + AddListItem(srv, net->servers); } - AddListItem(e, pendingnet); + AddListItem(net, pendingnet); } void free_pending_net(aClient *sptr) { -aPendingNet *e, *e_next; - - for (e = pendingnet; e; e = e_next) + aPendingNet *net, *net_next; + aPendingServer *srv, *srv_next; + + for (net = pendingnet; net; net = net_next) { - e_next = e->next; - if (e->sptr == sptr) + net_next = net->next; + if (net->sptr == sptr) { - DelListItem(e, pendingnet); - MyFree(e); + for (srv = net->servers; srv; srv = srv_next) + { + srv_next = srv->next; + MyFree(srv); + } + DelListItem(net, pendingnet); + MyFree(net); /* Don't break, there can be multiple objects */ } } } -aPendingNet *find_pending_net_by_numeric_butone(int numeric, aClient *exempt) +aPendingNet *find_pending_net_by_sid_butone(char *sid, aClient *exempt) { -aPendingNet *e; -int i; + aPendingNet *net; + aPendingServer *srv; - if (numeric <= 0) + if (BadPtr(sid)) return NULL; - for (e = pendingnet; e; e = e->next) + for (net = pendingnet; net; net = net->next) { - if (e->sptr == exempt) + if (net->sptr == exempt) continue; - for (i = 0; i < e->numservers; i++) - if (e->servers[i] == numeric) - return e; + for (srv = net->servers; srv; srv = srv->next) + if (!strcmp(srv->sid, sid)) + return net; } return NULL; } -/** Search the pending connections list for any identical numerics */ -aClient *find_pending_net_duplicates(aClient *cptr, aClient **srv, int *numeric) +/** Search the pending connections list for any identical sids */ +aClient *find_pending_net_duplicates(aClient *cptr, aClient **srv, char **sid) { -aPendingNet *e, *other; -int i; + aPendingNet *net, *other; + aPendingServer *s; *srv = NULL; - *numeric = 0; + *sid = NULL; - for (e = pendingnet; e; e = e->next) + for (net = pendingnet; net; net = net->next) { - if (e->sptr != cptr) + if (net->sptr != cptr) continue; /* Ok, found myself */ - for (i = 0; i < e->numservers; i++) + for (s = net->servers; s; s = s->next) { - int curr_numeric = e->servers[i]; - other = find_pending_net_by_numeric_butone(curr_numeric, cptr); + char *curr_sid = s->sid; + other = find_pending_net_by_sid_butone(curr_sid, cptr); if (other) { - *srv = e->sptr; - *numeric = curr_numeric; + *srv = net->sptr; + *sid = s->sid; return other->sptr; /* Found another (pending) server with identical numeric */ } } @@ -1363,3 +1360,26 @@ int i; return NULL; } + +aClient *find_non_pending_net_duplicates(aClient *cptr) +{ + aPendingNet *net; + aPendingServer *s; + aClient *acptr; + + for (net = pendingnet; net; net = net->next) + { + if (net->sptr != cptr) + continue; + /* Ok, found myself */ + for (s = net->servers; s; s = s->next) + { + acptr = find_server(s->sid, NULL); + if (acptr) + return acptr; /* Found another (fully CONNECTED) server with identical numeric */ + } + } + + return NULL; +} +