X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=blobdiff_plain;f=ircd%2Fm_server.c;h=aae44c5092c2a4f1e2274c70871d503c12a701d6;hp=baaaf582629b12d664e97ed728389d152bf40907;hb=384fa2237ccf0348190d655a6ea4cb8131d05354;hpb=ae91ef6320f611af74e70a0db2620c338fbaa7d5 diff --git a/ircd/m_server.c b/ircd/m_server.c index baaaf58..aae44c5 100644 --- a/ircd/m_server.c +++ b/ircd/m_server.c @@ -19,81 +19,22 @@ * 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_functions execute protocol messages on this server: - * - * cptr is always NON-NULL, pointing to a *LOCAL* client - * structure (with an open socket connected!). This - * identifies the physical socket where the message - * originated (or which caused the m_function to be - * executed--some m_functions may call others...). - * - * sptr is the source of the message, defined by the - * prefix part of the message if present. If not - * or prefix not found, then sptr==cptr. - * - * (!IsServer(cptr)) => (cptr == sptr), because - * prefixes are taken *only* from servers... - * - * (IsServer(cptr)) - * (sptr == cptr) => the message didn't - * have the prefix. - * - * (sptr != cptr && IsServer(sptr) means - * the prefix specified servername. (?) - * - * (sptr != cptr && !IsServer(sptr) means - * that message originated from a remote - * user (not local). - * - * combining - * - * (!IsServer(sptr)) means that, sptr can safely - * taken as defining the target structure of the - * message in this server. - * - * *Always* true (if 'parse' and others are working correct): - * - * 1) sptr->from == cptr (note: cptr->from == cptr) - * - * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr - * *cannot* be a local connection, unless it's - * actually cptr!). [MyConnect(x) should probably - * be defined as (x == x->from) --msa ] - * - * parc number of variable parameter strings (if zero, - * parv is allowed to be NULL) - * - * parv a NULL terminated list of parameter pointers, - * - * parv[0], sender (prefix string), if not present - * this points to an empty string. - * parv[1]...parv[parc-1] - * pointers to additional parameters - * parv[parc] == NULL, *always* - * - * note: it is guaranteed that parv[0]..parv[parc-1] are all - * non-NULL pointers. */ -#if 0 -/* - * No need to include handlers.h here the signatures must match - * and we don't need to force a rebuild of all the handlers everytime - * we add a new one to the list. --Bleep +/** @file + * @brief Handlers for the SERVER command. + * @version $Id$ */ -#include "handlers.h" -#endif /* 0 */ + +#include "config.h" + #include "client.h" -#include "crule.h" #include "hash.h" #include "ircd.h" #include "ircd_log.h" +#include "ircd_features.h" #include "ircd_reply.h" #include "ircd_string.h" +#include "jupe.h" #include "list.h" #include "match.h" #include "msg.h" @@ -108,103 +49,18 @@ #include "send.h" #include "userload.h" -#include +/* #include -- Now using assert in ircd_log.h */ #include #include -/* - * mr_server - registration message handler - * - * parv[0] = sender prefix - * parv[1] = servername - * parv[2] = hopcount - * parv[3] = start timestamp - * parv[4] = link timestamp - * parv[5] = major protocol version: P09/P10 - * parv[parc-1] = serverinfo - * If cptr is P10: - * parv[6] = "YMM", where 'Y' is the server numeric and "MM" is the - * numeric nick mask of this server. - * parv[7] = 0 (not used yet, mandatory unsigned int after u2.10.06) +/** Clean up a server name. + * @param[in] host Input server name. + * @return NULL if the name is invalid, else pointer to cleaned-up name. */ -int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +static char * +clean_servername(char *host) { char* ch; - int i; - char info[REALLEN + 1]; - char* host; - struct Client* acptr; - struct Client* bcptr; - struct Client* LHcptr = 0; - struct ConfItem* aconf = 0; - struct ConfItem* cconf; - struct ConfItem* lhconf = 0; - int hop; - int ret; - int active_lh_line = 0; - unsigned short prot; - time_t start_timestamp; - time_t timestamp = 0; - time_t recv_time; - time_t ghost = 0; - - if (IsUser(cptr)) - { - sendto_one(cptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]); - return 0; - } - - if (IsUserPort(cptr)) - return exit_client_msg(cptr, cptr, &me, - "Cannot connect a server to a user port"); - - recv_time = TStime(); - info[0] = '\0'; - - if (parc < 7) - { - return need_more_params(sptr, "SERVER"); - return exit_client(cptr, cptr, &me, "Need more parameters"); - } - ircd_log(L_NOTICE, "SERVER: %s %s[%s]", parv[1], cptr->sockhost, cptr->sock_ip); - host = parv[1]; - /* - * Detect protocol - */ - if (strlen(parv[5]) != 3 || (parv[5][0] != 'P' && parv[5][0] != 'J')) - return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]); - - if (!IsServer(cptr)) /* Don't allow silently connecting a server */ - *parv[5] = 'J'; - - prot = atoi(parv[5] + 1); - if (prot > atoi(MAJOR_PROTOCOL)) - prot = atoi(MAJOR_PROTOCOL); - /* - * Because the previous test is only in 2.10, the following is needed - * till all servers are 2.10: - */ - if (IsServer(cptr) && prot > Protocol(cptr)) - prot = Protocol(cptr); - hop = atoi(parv[2]); - start_timestamp = atoi(parv[3]); - timestamp = atoi(parv[4]); - Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age " TIME_T_FMT " (" - TIME_T_FMT ")", host, parv[4], start_timestamp, me.serv->timestamp)); - - if ((timestamp < OLDEST_TS || (hop == 1 && start_timestamp < OLDEST_TS))) - { - return exit_client_msg(cptr, sptr, &me, - "Bogus timestamps (%s %s)", parv[3], parv[4]); - } - ircd_strncpy(info, parv[parc - 1], REALLEN); - info[REALLEN] = '\0'; - if (prot < atoi(MINOR_PROTOCOL)) { - sendto_ops("Got incompatible protocol version (%s) from %s", - parv[5], cptr->name); - return exit_new_server(cptr, sptr, host, timestamp, - "Incompatible protocol: %s", parv[5]); - } /* * Check for "FRENCH " infection ;-) (actually this should * be replaced with routine to check the hostname syntax in @@ -216,157 +72,95 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) if (strlen(host) > HOSTLEN) host[HOSTLEN] = '\0'; - for (ch = host; *ch; ch++) { + for (ch = host; *ch; ch++) if (*ch <= ' ' || *ch > '~') break; - } - if (*ch || !strchr(host, '.')) { - sendto_ops("Bogus server name (%s) from %s", host, cptr->name); - return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host); - } + if (*ch || !strchr(host, '.') || strlen(host) > HOSTLEN) + return NULL; + return host; +} + +/** Parse protocol version from a string. + * @param[in] proto String version of protocol number. + * @return Zero if \a proto is unrecognized, else protocol version. + */ +static unsigned short +parse_protocol(const char *proto) +{ + unsigned short prot; + if (strlen(proto) != 3 || (proto[0] != 'P' && proto[0] != 'J')) + return 0; + prot = atoi(proto+1); + if (prot > atoi(MAJOR_PROTOCOL)) + prot = atoi(MAJOR_PROTOCOL); + return prot; +} + +/** Reason not to accept a server's new announcement. */ +enum lh_type { + ALLOWED, /**< The new server link is accepted. */ + MAX_HOPS_EXCEEDED, /**< The path to the server is too long. */ + NOT_ALLOWED_TO_HUB, /**< My peer is not allowed to hub for the server. */ + I_AM_NOT_HUB /**< I have another active server link but not FEAT_HUB. */ +}; + +/** Check whether the introduction of a new server would cause a loop + * or be disallowed by leaf and hub configuration directives. + * @param[in] cptr Neighbor who sent the message. + * @param[in] sptr Client that originated the message. + * @param[out] ghost If non-NULL, receives ghost timestamp for new server. + * @param[in] host Name of new server. + * @param[in] numnick Numnick mask of new server. + * @param[in] timestamp Claimed link timestamp of new server. + * @param[in] hop Number of hops to the new server. + * @param[in] junction Non-zero if the new server is still bursting. + * @return CPTR_KILLED if \a cptr was SQUIT. 0 if some other server + * was SQUIT. 1 if the new server is allowed. + */ +static int +check_loop_and_lh(struct Client* cptr, struct Client *sptr, time_t *ghost, const char *host, const char *numnick, time_t timestamp, int hop, int junction) +{ + struct Client* acptr; + struct Client* LHcptr = NULL; + struct ConfItem* lhconf; + enum lh_type active_lh_line = ALLOWED; + int ii; - if (IsServer(cptr)) + if (ghost) + *ghost = 0; + + /* + * Calculate type of connect limit and applicable config item. + */ + lhconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER); + assert(lhconf != NULL); + if (ghost) { - /* - * A local server introduces a new server behind this link. - * Check if this is allowed according L:, H: and Q: lines. - */ - if (info[0] == '\0') - return exit_client_msg(cptr, cptr, &me, - "No server info specified for %s", host); - /* - * See if the newly found server is behind a guaranteed - * leaf (L-line). If so, close the link. - */ - if ((lhconf = find_conf_byhost(cptr->confs, cptr->name, CONF_LEAF)) && - (!lhconf->port || (hop > lhconf->port))) - { - /* - * L: lines normally come in pairs, here we try to - * make sure that the oldest link is squitted, not - * both. - */ - active_lh_line = 1; - if (timestamp <= cptr->serv->timestamp) - LHcptr = 0; /* Kill incoming server */ - else - LHcptr = cptr; /* Squit ourselfs */ - } - else if (!(lhconf = find_conf_byname(cptr->confs, cptr->name, CONF_HUB)) || - (lhconf->port && (hop > lhconf->port))) - { - struct Client *ac3ptr; - active_lh_line = 2; - /* Look for net junction causing this: */ - LHcptr = 0; /* incoming server */ - if (*parv[5] != 'J') { - for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up) { - if (IsJunction(ac3ptr)) { - LHcptr = ac3ptr; - break; - } + if (!feature_bool(FEAT_HUB)) + for (ii = 0; ii <= HighestFd; ii++) + if (LocalClientArray[ii] && IsServer(LocalClientArray[ii])) { + active_lh_line = I_AM_NOT_HUB; + break; } - } - } } - - if (IsUnknown(cptr) || IsHandshake(cptr)) + else if (hop > lhconf->maximum) { - const char* encr; - - /* - * A local link that is still in undefined state wants - * to be a SERVER. Check if this is allowed and change - * status accordingly... - */ - /* - * If there is more then one server on the same machine - * that we try to connect to, it could be that the /CONNECT - * caused this connect to be put at the wrong place - * in the hashtable. --Run - * Same thing for Unknown connections that first send NICK. - * --Xorath - * Better check if the two strings are (caseless) identical - * and not mess with hash internals. - * --Nemesi + /* Because "maximum" should be 0 for non-hub links, check whether + * there is a hub mask -- if not, complain that the server isn't + * allowed to hub. */ - if (!EmptyString(cptr->name) && - (IsUnknown(cptr) || IsHandshake(cptr)) && - 0 != ircd_strcmp(cptr->name, host)) - hChangeClient(cptr, host); - ircd_strncpy(cptr->name, host, HOSTLEN); - ircd_strncpy(cptr->info, info[0] ? info : me.name, REALLEN); - cptr->hopcount = hop; - - /* check connection rules */ - for (cconf = GlobalConfList; cconf; cconf = cconf->next) { - if ((cconf->status == CONF_CRULEALL) && (match(cconf->host, host) == 0)) { - if (crule_eval(cconf->passwd)) { - ServerStats->is_ref++; - sendto_ops("Refused connection from %s.", cptr->name); - return exit_client(cptr, cptr, &me, "Disallowed by connection rule"); + active_lh_line = lhconf->hub_limit ? MAX_HOPS_EXCEEDED : NOT_ALLOWED_TO_HUB; + } + else if (lhconf->hub_limit && match(lhconf->hub_limit, host)) + { + struct Client *ac3ptr; + active_lh_line = NOT_ALLOWED_TO_HUB; + if (junction) + for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) + if (IsJunction(ac3ptr)) { + LHcptr = ac3ptr; + break; } - } - } - if (conf_check_server(cptr)) { - ++ServerStats->is_ref; - sendto_ops("Received unauthorized connection from %s.", cptr->name); - return exit_client(cptr, cptr, &me, "No C:line"); - } - - host = cptr->name; - - update_load(); - - if (!(aconf = find_conf_byname(cptr->confs, host, CONF_SERVER))) { - ++ServerStats->is_ref; -#ifndef GODMODE - sendto_ops("Access denied. No conf line for server %s", cptr->name); - return exit_client_msg(cptr, cptr, &me, - "Access denied. No conf line for server %s", cptr->name); -#else /* GODMODE */ - sendto_ops("General C: line active: No line for server %s", cptr->name); - aconf = find_conf_byname(cptr->confs, "general.undernet.org", CONF_SERVER); - if (!aconf) { - sendto_ops("Neither C lines for server %s nor " - "\"general.undernet.org\"", cptr->name); - return exit_client_msg(cptr, cptr, &me, "No C lines for server %s", cptr->name); - } -#endif /* GODMODE */ - } -#ifdef CRYPT_LINK_PASSWORD - /* passwd may be NULL. Head it off at the pass... */ - if (*cptr->passwd) { - char salt[3]; - - salt[0] = aconf->passwd[0]; - salt[1] = aconf->passwd[1]; - salt[2] = '\0'; - encr = ircd_crypt(cptr->passwd, salt); - } - else - encr = ""; -#else - encr = cptr->passwd; -#endif /* CRYPT_LINK_PASSWORD */ -#ifndef GODMODE - if (*aconf->passwd && !!strcmp(aconf->passwd, encr)) { - ++ServerStats->is_ref; - sendto_ops("Access denied (passwd mismatch) %s", cptr->name); - return exit_client_msg(cptr, cptr, &me, - "No Access (passwd mismatch) %s", cptr->name); - } -#endif /* not GODMODE */ - memset(cptr->passwd, 0, sizeof(cptr->passwd)); - -#ifndef HUB - for (i = 0; i <= HighestFd; i++) - if (LocalClientArray[i] && IsServer(LocalClientArray[i])) { - active_lh_line = 3; - LHcptr = 0; - break; - } -#endif } /* @@ -376,8 +170,8 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) * servers - these shouldn't happen anymore when all upgraded to 2.10. * -- Run */ - while ((acptr = FindClient(host)) || - (parc > 7 && (acptr = FindNServer(parv[6])))) + while ((acptr = FindClient(host)) + || (numnick && (acptr = FindNServer(numnick)))) { /* * This link is trying feed me a server that I already have @@ -393,24 +187,25 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) * If my ircd.conf sucks, I can try to connect to myself: */ if (acptr == &me) - return exit_client_msg(cptr, cptr, &me, "nick collision with me (%s)", host); + return exit_client_msg(cptr, cptr, &me, "nick collision with me (%s), check server number in M:?", host); /* * Detect wrong numeric. */ - if (0 != ircd_strcmp(acptr->name, host)) { - sendto_serv_butone(cptr, - ":%s WALLOPS :SERVER Numeric Collision: %s != %s", - me.name, acptr->name, host); + if (0 != ircd_strcmp(cli_name(acptr), host)) + { + sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr, + ":SERVER Numeric Collision: %s != %s", + cli_name(acptr), host); return exit_client_msg(cptr, cptr, &me, "NUMERIC collision between %s and %s." - " Is your server numeric correct ?", host, acptr->name); + " Is your server numeric correct ?", host, cli_name(acptr)); } /* * Kill our try, if we had one. */ if (IsConnecting(acptr)) { - if (!active_lh_line && exit_client(cptr, acptr, &me, + if (active_lh_line == ALLOWED && exit_client(cptr, acptr, &me, "Just connected via another link") == CPTR_KILLED) return CPTR_KILLED; /* @@ -422,7 +217,7 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) } /* * Avoid other nick collisions... - * This is a doubtfull test though, what else would it be + * This is a doubtful test though, what else would it be * when it has a server.name ? */ else if (!IsServer(acptr) && !IsHandshake(acptr)) @@ -432,14 +227,15 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) * Our new server might be a juped server, * or someone trying abuse a second Uworld: */ - else if (IsServer(acptr) && (0 == ircd_strncmp(acptr->info, "JUPE", 4) || - find_conf_byhost(cptr->confs, acptr->name, CONF_UWORLD))) + else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4) || + find_conf_byhost(cli_confs(cptr), cli_name(acptr), CONF_UWORLD))) { if (!IsServer(sptr)) - return exit_client(cptr, sptr, &me, acptr->info); - sendto_one(cptr, ":%s WALLOPS :Received :%s SERVER %s from %s !?!", - me.name, parv[0], parv[1], cptr->name); - return exit_new_server(cptr, sptr, host, timestamp, "%s", acptr->info); + return exit_client(cptr, sptr, &me, cli_info(acptr)); + sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr, + ":Received :%s SERVER %s from %s !?!", + NumServ(cptr), host, cli_name(cptr)); + return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr)); } /* * Of course we find the handshake this link was before :) @@ -460,65 +256,62 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) struct Client* ac3ptr; /* Search youngest link: */ - for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up) - if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp) + for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) + if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp) c3ptr = ac3ptr; if (IsServer(sptr)) { - for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up) - if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp) + for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up) + if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp) c3ptr = ac3ptr; } - if (timestamp > c3ptr->serv->timestamp) + if (timestamp > cli_serv(c3ptr)->timestamp) { c3ptr = 0; c2ptr = acptr; /* Make sure they differ */ } /* Search second youngest link: */ - for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up) + for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up) if (ac2ptr != c3ptr && - ac2ptr->serv->timestamp > - (c2ptr ? c2ptr->serv->timestamp : timestamp)) + cli_serv(ac2ptr)->timestamp > + (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = ac2ptr; if (IsServer(sptr)) { - for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up) + for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up) if (ac2ptr != c3ptr && - ac2ptr->serv->timestamp > - (c2ptr ? c2ptr->serv->timestamp : timestamp)) + cli_serv(ac2ptr)->timestamp > + (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = ac2ptr; } - if (c3ptr && timestamp > (c2ptr ? c2ptr->serv->timestamp : timestamp)) + if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp)) c2ptr = 0; /* If timestamps are equal, decide which link to break * by name. */ - if ((c2ptr ? c2ptr->serv->timestamp : timestamp) == - (c3ptr ? c3ptr->serv->timestamp : timestamp)) + if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) == + (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp)) { - char* n2; - char* n2up; - char* n3; - char* n3up; + const char *n2, *n2up, *n3, *n3up; if (c2ptr) { - n2 = c2ptr->name; - n2up = MyConnect(c2ptr) ? me.name : c2ptr->serv->up->name; + n2 = cli_name(c2ptr); + n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up); } else { n2 = host; - n2up = IsServer(sptr) ? sptr->name : me.name; + n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me); } if (c3ptr) { - n3 = c3ptr->name; - n3up = MyConnect(c3ptr) ? me.name : c3ptr->serv->up->name; + n3 = cli_name(c3ptr); + n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up); } else { n3 = host; - n3up = IsServer(sptr) ? sptr->name : me.name; + n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me); } if (strcmp(n2, n2up) > 0) n2 = n2up; @@ -535,11 +328,11 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) if (!c2ptr) return exit_new_server(cptr, sptr, host, timestamp, "server %s already exists and is %ld seconds younger.", - host, (long)acptr->serv->timestamp - (long)timestamp); - else if (c2ptr->from == cptr || IsServer(sptr)) + host, (long)cli_serv(acptr)->timestamp - (long)timestamp); + else if (cli_from(c2ptr) == cptr || IsServer(sptr)) { - struct Client *killedptrfrom = c2ptr->from; - if (active_lh_line) + struct Client *killedptrfrom = cli_from(c2ptr); + if (active_lh_line != ALLOWED) { /* * If the L: or H: line also gets rid of this link, @@ -551,8 +344,8 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) * If breaking the loop here solves the L: or H: * line problem, we don't squit that. */ - if (c2ptr->from == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr))) - active_lh_line = 0; + if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr))) + active_lh_line = ALLOWED; else { /* @@ -570,16 +363,16 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) * a Ghost... (20 seconds is more then enough because all * SERVER messages are at the beginning of a net.burst). --Run */ - if (CurrentTime - cptr->serv->ghost < 20) + if (CurrentTime - cli_serv(cptr)->ghost < 20) { - killedptrfrom = acptr->from; + killedptrfrom = cli_from(acptr); if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED) return CPTR_KILLED; } else if (exit_client_msg(cptr, c2ptr, &me, "Loop <-- %s (new link is %ld seconds younger)", host, - (c3ptr ? (long)c3ptr->serv->timestamp : timestamp) - - (long)c2ptr->serv->timestamp) == CPTR_KILLED) + (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) - + (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED) return CPTR_KILLED; /* * Did we kill the incoming server off already ? @@ -589,12 +382,12 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) } else { - if (active_lh_line) + if (active_lh_line != ALLOWED) { if (LHcptr && a_kills_b_too(LHcptr, acptr)) break; - if (acptr->from == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr))) - active_lh_line = 0; + if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr))) + active_lh_line = ALLOWED; else { LHcptr = 0; @@ -608,7 +401,8 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) * at the second youngest link, assuming it isn't * a REAL loop. */ - ghost = CurrentTime; /* Mark that it caused a ghost */ + if (ghost) + *ghost = CurrentTime; /* Mark that it caused a ghost */ if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED) return CPTR_KILLED; break; @@ -616,1483 +410,377 @@ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) } } - if (active_lh_line) + if (active_lh_line != ALLOWED) { - if (LHcptr == 0) { - return exit_new_server(cptr, sptr, host, timestamp, - (active_lh_line == 2) ? "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)", - cptr->name, host, - lhconf ? (lhconf->name ? lhconf->name : "*") : "!"); - } - else + if (!LHcptr) + LHcptr = sptr; + if (active_lh_line == MAX_HOPS_EXCEEDED) { - int killed = a_kills_b_too(LHcptr, sptr); - if (active_lh_line < 3) - { - if (exit_client_msg(cptr, LHcptr, &me, - (active_lh_line == 2) ? "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)", - cptr->name, host, - lhconf ? (lhconf->name ? lhconf->name : "*") : "!") == CPTR_KILLED) - return CPTR_KILLED; - } - else - { - ServerStats->is_ref++; - if (exit_client(cptr, LHcptr, &me, "I'm a leaf") == CPTR_KILLED) - return CPTR_KILLED; - } - /* - * Did we kill the incoming server off already ? - */ - if (killed) - return 0; + return exit_client_msg(cptr, LHcptr, &me, + "Maximum hops exceeded for %s at %s", + cli_name(cptr), host); } - } - - if (IsServer(cptr)) - { - /* - * 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... - */ - - acptr = make_client(cptr, STAT_SERVER); - make_server(acptr); - acptr->serv->prot = prot; - acptr->serv->timestamp = timestamp; - acptr->hopcount = hop; - ircd_strncpy(acptr->name, host, HOSTLEN); - ircd_strncpy(acptr->info, info, REALLEN); - acptr->serv->up = sptr; - acptr->serv->updown = add_dlink(&sptr->serv->down, acptr); - /* Use cptr, because we do protocol 9 -> 10 translation - for numeric nicks ! */ - SetServerYXX(cptr, acptr, parv[6]); - - Count_newremoteserver(UserStats); - if (Protocol(acptr) < 10) - acptr->flags |= FLAGS_TS8; - add_client_to_list(acptr); - hAddClient(acptr); - if (*parv[5] == 'J') + else if (active_lh_line == NOT_ALLOWED_TO_HUB) { - SetBurst(acptr); - sendto_op_mask(SNO_NETWORK, "Net junction: %s %s", - sptr->name, acptr->name); - SetJunction(acptr); + return exit_client_msg(cptr, LHcptr, &me, + "%s is not allowed to hub for %s", + cli_name(cptr), host); } - /* - * Old sendto_serv_but_one() call removed because we now need to send - * different names to different servers (domain name matching). - */ - for (i = 0; i <= HighestFd; i++) + else /* I_AM_NOT_HUB */ { - if (!(bcptr = LocalClientArray[i]) || !IsServer(bcptr) || - bcptr == cptr || IsMe(bcptr)) - continue; - if (0 == match(me.name, acptr->name)) - continue; - sendto_one(bcptr, "%s " TOK_SERVER " %s %d 0 %s %s %s%s 0 :%s", - NumServ(sptr), acptr->name, hop + 1, parv[4], parv[5], - NumServCap(acptr), acptr->info); + ServerStats->is_ref++; + return exit_client(cptr, LHcptr, &me, "I'm a leaf, define the HUB feature"); } - return 0; } - if (IsUnknown(cptr) || IsHandshake(cptr)) - { - make_server(cptr); - cptr->serv->timestamp = timestamp; - cptr->serv->prot = prot; - cptr->serv->ghost = ghost; - SetServerYXX(cptr, cptr, parv[6]); - if (start_timestamp > OLDEST_TS) - { -#ifndef RELIABLE_CLOCK -#ifdef TESTNET - sendto_ops("Debug: my start time: " TIME_T_FMT " ; others start time: " - TIME_T_FMT, me.serv->timestamp, start_timestamp); - sendto_ops("Debug: receive time: " TIME_T_FMT " ; received timestamp: " - TIME_T_FMT " ; difference %ld", - recv_time, timestamp, timestamp - recv_time); -#endif - if (start_timestamp < me.serv->timestamp) - { - sendto_ops("got earlier start time: " TIME_T_FMT " < " TIME_T_FMT, - start_timestamp, me.serv->timestamp); - me.serv->timestamp = start_timestamp; - TSoffset += timestamp - recv_time; - sendto_ops("clock adjusted by adding %d", (int)(timestamp - recv_time)); - } - else if ((start_timestamp > me.serv->timestamp) && IsUnknown(cptr)) - cptr->serv->timestamp = TStime(); + return 1; +} - else if (timestamp != recv_time) - { - /* - * Equal start times, we have a collision. Let the connected-to server - * decide. This assumes leafs issue more than half of the connection - * attempts. - */ - if (IsUnknown(cptr)) - cptr->serv->timestamp = TStime(); - else if (IsHandshake(cptr)) - { - sendto_ops("clock adjusted by adding %d", - (int)(timestamp - recv_time)); - TSoffset += timestamp - recv_time; - } - } -#else /* RELIABLE CLOCK IS TRUE, we _always_ use our own clock */ - if (start_timestamp < me.serv->timestamp) - me.serv->timestamp = start_timestamp; - if (IsUnknown(cptr)) - cptr->serv->timestamp = TStime(); -#endif +/** Update server start timestamps and TS offsets. + * @param[in] cptr Server that just connected. + * @param[in] timestamp Current time according to \a cptr. + * @param[in] start_timestamp Time that \a cptr started. + * @param[in] recv_time Current time as we know it. + */ +static void +check_start_timestamp(struct Client *cptr, time_t timestamp, time_t start_timestamp, time_t recv_time) +{ + Debug((DEBUG_DEBUG, "My start time: %Tu; other's start time: %Tu", + cli_serv(&me)->timestamp, start_timestamp)); + Debug((DEBUG_DEBUG, "Receive time: %Tu; received timestamp: %Tu; " + "difference %ld", recv_time, timestamp, timestamp - recv_time)); + if (feature_bool(FEAT_RELIABLE_CLOCK)) { + if (start_timestamp < cli_serv(&me)->timestamp) + cli_serv(&me)->timestamp = start_timestamp; + if (IsUnknown(cptr)) + cli_serv(cptr)->timestamp = TStime(); + } else if (start_timestamp < cli_serv(&me)->timestamp) { + sendto_opmask_butone(0, SNO_OLDSNO, "got earlier start time: " + "%Tu < %Tu", start_timestamp, + cli_serv(&me)->timestamp); + cli_serv(&me)->timestamp = start_timestamp; + TSoffset += timestamp - recv_time; + sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d", + (int)(timestamp - recv_time)); + } else if ((start_timestamp > cli_serv(&me)->timestamp) && + IsUnknown(cptr)) { + cli_serv(cptr)->timestamp = TStime(); + } else if (timestamp != recv_time) { + /* + * Equal start times, we have a collision. Let the connected-to + * server decide. This assumes leafs issue more than half of the + * connection attempts. + */ + if (IsUnknown(cptr)) + cli_serv(cptr)->timestamp = TStime(); + else if (IsHandshake(cptr)) { + sendto_opmask_butone(0, SNO_OLDSNO, "clock adjusted by adding %d", + (int)(timestamp - recv_time)); + TSoffset += timestamp - recv_time; } - - ret = server_estab(cptr, aconf); - } - else - ret = 0; -#ifdef RELIABLE_CLOCK - if (abs(cptr->serv->timestamp - recv_time) > 30) - { - sendto_ops("Connected to a net with a timestamp-clock" - " difference of " STIME_T_FMT " seconds! Used SETTIME to correct" - " this.", timestamp - recv_time); - sendto_one(cptr, ":%s SETTIME " TIME_T_FMT " :%s", - me.name, TStime(), me.name); } -#endif +} - return ret; +/** Interpret a server's flags. + * + * @param[in] cptr New server structure. + * @param[in] flags String listing server's P10 flags. + */ +void set_server_flags(struct Client *cptr, const char *flags) +{ + while (*flags) switch (*flags++) { + case 'h': SetHub(cptr); break; + case 's': SetService(cptr); break; + case '6': SetIPv6(cptr); break; + } } -/* - * ms_server - server message handler +/** Handle a SERVER message from an unregistered connection. * - * parv[0] = sender prefix - * parv[1] = servername - * parv[2] = hopcount - * parv[3] = start timestamp - * parv[4] = link timestamp - * parv[5] = major protocol version: P09/P10 - * parv[parc-1] = serverinfo - * If cptr is P10: - * parv[6] = "YMM", where 'Y' is the server numeric and "MM" is the - * numeric nick mask of this server. - * parv[7] = 0 (not used yet, mandatory unsigned int after u2.10.06) + * \a parv has the following elements: + * \li \a parv[1] is the server name + * \li \a parv[2] is the hop count to the server + * \li \a parv[3] is the start timestamp for the server + * \li \a parv[4] is the link timestamp + * \li \a parv[5] is the protocol version (P10 or J10) + * \li \a parv[6] is the numnick mask for the server + * \li \a parv[7] is a string of flags like +hs to mark hubs and services + * \li \a parv[\a parc - 1] is the server description + * + * See @ref m_functions for discussion of the arguments. + * @param[in] cptr Client that sent us the message. + * @param[in] sptr Original source of message. + * @param[in] parc Number of arguments. + * @param[in] parv Argument vector. */ -int ms_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - char* ch; - int i; - char info[REALLEN + 1]; char* host; - struct Client* acptr; - struct Client* bcptr; - struct Client* LHcptr = 0; - struct ConfItem* aconf = 0; - struct ConfItem* cconf; - struct ConfItem* lhconf = 0; + struct ConfItem* aconf; + struct Jupe* ajupe; int hop; int ret; - int active_lh_line = 0; unsigned short prot; time_t start_timestamp; - time_t timestamp = 0; + time_t timestamp; time_t recv_time; - time_t ghost = 0; - - if (IsUser(cptr)) - { - sendto_one(cptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]); - return 0; - } + time_t ghost; if (IsUserPort(cptr)) return exit_client_msg(cptr, cptr, &me, "Cannot connect a server to a user port"); - recv_time = TStime(); - info[0] = '\0'; - if (parc < 7) + if (parc < 8) { - return need_more_params(sptr, "SERVER"); + need_more_params(sptr, "SERVER"); return exit_client(cptr, cptr, &me, "Need more parameters"); } - host = parv[1]; - /* - * Detect protocol - */ - if (strlen(parv[5]) != 3 || (parv[5][0] != 'P' && parv[5][0] != 'J')) - return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]); + host = clean_servername(parv[1]); + if (!host) + { + sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s", + host, cli_name(cptr)); + return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host); + } - if (!IsServer(cptr)) /* Don't allow silently connecting a server */ - *parv[5] = 'J'; + if ((ajupe = jupe_find(host)) && JupeIsActive(ajupe)) + return exit_client_msg(cptr, sptr, &me, "Juped: %s", JupeReason(ajupe)); + + /* check connection rules */ + if (0 != conf_eval_crule(host, CRULE_ALL)) { + ServerStats->is_ref++; + sendto_opmask_butone(0, SNO_OLDSNO, "Refused connection from %s.", cli_name(cptr)); + return exit_client(cptr, cptr, &me, "Disallowed by connection rule"); + } + + log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "SERVER: %s %s[%s]", host, + cli_sockhost(cptr), cli_sock_ip(cptr)); - prot = atoi(parv[5] + 1); - if (prot > atoi(MAJOR_PROTOCOL)) - prot = atoi(MAJOR_PROTOCOL); /* - * Because the previous test is only in 2.10, the following is needed - * till all servers are 2.10: + * Detect protocol */ - if (IsServer(cptr) && prot > Protocol(cptr)) - prot = Protocol(cptr); hop = atoi(parv[2]); start_timestamp = atoi(parv[3]); timestamp = atoi(parv[4]); - Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age " TIME_T_FMT " (" - TIME_T_FMT ")", host, parv[4], start_timestamp, me.serv->timestamp)); - if ((timestamp < OLDEST_TS || (hop == 1 && start_timestamp < OLDEST_TS))) - { - return exit_client_msg(cptr, sptr, &me, - "Bogus timestamps (%s %s)", parv[3], parv[4]); - } - ircd_strncpy(info, parv[parc - 1], REALLEN); - info[REALLEN] = '\0'; - if (prot < atoi(MINOR_PROTOCOL)) { - sendto_ops("Got incompatible protocol version (%s) from %s", - parv[5], cptr->name); + prot = parse_protocol(parv[5]); + if (!prot) + return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]); + else if (prot < atoi(MINOR_PROTOCOL)) return exit_new_server(cptr, sptr, host, timestamp, "Incompatible protocol: %s", parv[5]); - } - /* - * Check for "FRENCH " infection ;-) (actually this should - * be replaced with routine to check the hostname syntax in - * general). [ This check is still needed, even after the parse - * is fixed, because someone can send "SERVER :foo bar " ]. - * Also, changed to check other "difficult" characters, now - * that parse lets all through... --msa - */ - if (strlen(host) > HOSTLEN) - host[HOSTLEN] = '\0'; - for (ch = host; *ch; ch++) - if (*ch <= ' ' || *ch > '~') - break; - if (*ch || !strchr(host, '.')) { - sendto_ops("Bogus server name (%s) from %s", host, cptr->name); - return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host); + + Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)", + host, parv[4], start_timestamp, cli_serv(&me)->timestamp)); + + if (timestamp < OLDEST_TS || start_timestamp < OLDEST_TS) + return exit_client_msg(cptr, sptr, &me, + "Bogus timestamps (%s %s)", parv[3], parv[4]); + + /* If the server had a different name before, change it. */ + if (!EmptyString(cli_name(cptr)) && + (IsUnknown(cptr) || IsHandshake(cptr)) && + 0 != ircd_strcmp(cli_name(cptr), host)) + hChangeClient(cptr, host); + ircd_strncpy(cli_name(cptr), host, HOSTLEN); + ircd_strncpy(cli_info(cptr), parv[parc-1][0] ? parv[parc-1] : cli_name(&me), REALLEN); + cli_hopcount(cptr) = hop; + + if (conf_check_server(cptr)) { + ++ServerStats->is_ref; + sendto_opmask_butone(0, SNO_OLDSNO, "Received unauthorized connection " + "from %s.", cli_name(cptr)); + log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "Received unauthorized " + "connection from %C [%s]", cptr, + ircd_ntoa(&cli_ip(cptr))); + return exit_client(cptr, cptr, &me, "No Connect block"); + } + + host = cli_name(cptr); + + update_load(); + + if (!(aconf = find_conf_byname(cli_confs(cptr), host, CONF_SERVER))) { + ++ServerStats->is_ref; + sendto_opmask_butone(0, SNO_OLDSNO, "Access denied. No conf line for " + "server %s", cli_name(cptr)); + return exit_client_msg(cptr, cptr, &me, + "Access denied. No conf line for server %s", cli_name(cptr)); } - if (IsServer(cptr)) - { - /* - * A local server introduces a new server behind this link. - * Check if this is allowed according L:, H: and Q: lines. - */ - if (info[0] == '\0') - return exit_client_msg(cptr, cptr, &me, - "No server info specified for %s", host); - /* - * See if the newly found server is behind a guaranteed - * leaf (L-line). If so, close the link. - */ - if ((lhconf = find_conf_byhost(cptr->confs, cptr->name, CONF_LEAF)) && - (!lhconf->port || (hop > lhconf->port))) - { - /* - * L: lines normally come in pairs, here we try to - * make sure that the oldest link is squitted, not - * both. - */ - active_lh_line = 1; - if (timestamp <= cptr->serv->timestamp) - LHcptr = 0; /* Kill incoming server */ - else - LHcptr = cptr; /* Squit ourselfs */ - } - else if (!(lhconf = find_conf_byname(cptr->confs, cptr->name, CONF_HUB)) || - (lhconf->port && (hop > lhconf->port))) - { - struct Client *ac3ptr; - active_lh_line = 2; - /* Look for net junction causing this: */ - LHcptr = 0; /* incoming server */ - if (*parv[5] != 'J') { - for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up) { - if (IsJunction(ac3ptr)) { - LHcptr = ac3ptr; - break; - } - } - } - } + if (*aconf->passwd && !!strcmp(aconf->passwd, cli_passwd(cptr))) { + ++ServerStats->is_ref; + sendto_opmask_butone(0, SNO_OLDSNO, "Access denied (passwd mismatch) %s", + cli_name(cptr)); + return exit_client_msg(cptr, cptr, &me, + "No Access (passwd mismatch) %s", cli_name(cptr)); } - if (IsUnknown(cptr) || IsHandshake(cptr)) - { - const char* encr; + memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr))); - /* - * A local link that is still in undefined state wants - * to be a SERVER. Check if this is allowed and change - * status accordingly... - */ - /* - * If there is more then one server on the same machine - * that we try to connect to, it could be that the /CONNECT - * caused this connect to be put at the wrong place - * in the hashtable. --Run - * Same thing for Unknown connections that first send NICK. - * --Xorath - * Better check if the two strings are (caseless) identical - * and not mess with hash internals. - * --Nemesi - */ - if ((!(EmptyString(cptr->name))) - && (IsUnknown(cptr) || IsHandshake(cptr)) - && 0 != ircd_strcmp(cptr->name, host)) - hChangeClient(cptr, host); - ircd_strncpy(cptr->name, host, HOSTLEN); - ircd_strncpy(cptr->info, info[0] ? info : me.name, REALLEN); - cptr->hopcount = hop; + ret = check_loop_and_lh(cptr, sptr, &ghost, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, 1); + if (ret != 1) + return ret; - /* check connection rules */ - for (cconf = GlobalConfList; cconf; cconf = cconf->next) { - if ((cconf->status == CONF_CRULEALL) && (match(cconf->host, host) == 0)) { - if (crule_eval(cconf->passwd)) - { - ServerStats->is_ref++; - sendto_ops("Refused connection from %s.", cptr->name); - return exit_client(cptr, cptr, &me, "Disallowed by connection rule"); - } - } - } - if (conf_check_server(cptr)) { - ++ServerStats->is_ref; - sendto_ops("Received unauthorized connection from %s.", cptr->name); - return exit_client(cptr, cptr, &me, "No C conf lines"); - } + make_server(cptr); + cli_serv(cptr)->timestamp = timestamp; + cli_serv(cptr)->prot = prot; + cli_serv(cptr)->ghost = ghost; + memset(cli_privs(cptr), 255, sizeof(struct Privs)); + ClrPriv(cptr, PRIV_SET); + SetServerYXX(cptr, cptr, parv[6]); - host = cptr->name; + /* Attach any necessary UWorld config items. */ + attach_confs_byhost(cptr, host, CONF_UWORLD); - update_load(); + if (*parv[7] == '+') + set_server_flags(cptr, parv[7] + 1); - if (!(aconf = find_conf_byname(cptr->confs, host, CONF_SERVER))) { - ++ServerStats->is_ref; -#ifndef GODMODE - sendto_ops("Access denied. No conf line for server %s", cptr->name); - return exit_client_msg(cptr, cptr, &me, - "Access denied. No conf line for server %s", cptr->name); -#else /* GODMODE */ - sendto_ops("General C line active: No line for server %s", cptr->name); - aconf = - find_conf_byname(cptr->confs, "general.undernet.org", CONF_SERVER); - if (!aconf) { - sendto_ops("Neither C lines for server %s nor " - "\"general.undernet.org\"", cptr->name); - return exit_client_msg(cptr, cptr, &me, - "No C lines for server %s", cptr->name); - } -#endif /* GODMODE */ - } -#ifdef CRYPT_LINK_PASSWORD - /* passwd may be NULL. Head it off at the pass... */ - if (*cptr->passwd) - { - char salt[3]; + recv_time = TStime(); + check_start_timestamp(cptr, timestamp, start_timestamp, recv_time); + ret = server_estab(cptr, aconf); - salt[0] = aconf->passwd[0]; - salt[1] = aconf->passwd[1]; - salt[2] = '\0'; - encr = ircd_crypt(cptr->passwd, salt); - } - else - encr = ""; -#else - encr = cptr->passwd; -#endif /* CRYPT_LINK_PASSWORD */ -#ifndef GODMODE - if (*aconf->passwd && !!strcmp(aconf->passwd, encr)) { - ++ServerStats->is_ref; - sendto_ops("Access denied (passwd mismatch) %s", cptr->name); - return exit_client_msg(cptr, cptr, &me, - "No Access (passwd mismatch) %s", cptr->name); - } -#endif /* not GODMODE */ - memset(cptr->passwd, 0, sizeof(cptr->passwd)); - -#ifndef HUB - for (i = 0; i <= HighestFd; i++) - if (LocalClientArray[i] && IsServer(LocalClientArray[i])) { - active_lh_line = 3; - LHcptr = 0; - break; - } -#endif - } - - /* - * We want to find IsConnecting() and IsHandshake() too, - * use FindClient(). - * The second finds collisions with numeric representation of existing - * servers - these shouldn't happen anymore when all upgraded to 2.10. - * -- Run - */ - while ((acptr = FindClient(host)) || - (parc > 7 && (acptr = FindNServer(parv[6])))) - { - /* - * This link is trying feed me a server that I already have - * access through another path - * - * Do not allow Uworld to do this. - * Do not allow servers that are juped. - * Do not allow servers that have older link timestamps - * then this try. - * Do not allow servers that use the same numeric as an existing - * server, but have a different name. - * - * If my ircd.conf sucks, I can try to connect to myself: - */ - if (acptr == &me) - return exit_client_msg(cptr, cptr, &me, - "nick collision with me (%s)", host); - /* - * Detect wrong numeric. - */ - if (0 != ircd_strcmp(acptr->name, host)) - { - sendto_serv_butone(cptr, - ":%s WALLOPS :SERVER Numeric Collision: %s != %s", - me.name, acptr->name, host); - return exit_client_msg(cptr, cptr, &me, - "NUMERIC collision between %s and %s." - " Is your server numeric correct ?", host, acptr->name); - } - /* - * Kill our try, if we had one. - */ - if (IsConnecting(acptr)) - { - if (!active_lh_line && exit_client(cptr, acptr, &me, - "Just connected via another link") == CPTR_KILLED) - return CPTR_KILLED; - /* - * We can have only ONE 'IsConnecting', 'IsHandshake' or - * 'IsServer', because new 'IsConnecting's are refused to - * the same server if we already had it. - */ - break; - } - /* - * Avoid other nick collisions... - * This is a doubtfull test though, what else would it be - * when it has a server.name ? - */ - else if (!IsServer(acptr) && !IsHandshake(acptr)) - return exit_client_msg(cptr, cptr, &me, - "Nickname %s already exists!", host); - /* - * Our new server might be a juped server, - * or someone trying abuse a second Uworld: - */ - else if (IsServer(acptr) && (0 == ircd_strncmp(acptr->info, "JUPE", 4) || - find_conf_byhost(cptr->confs, acptr->name, CONF_UWORLD))) - { - if (!IsServer(sptr)) - return exit_client(cptr, sptr, &me, acptr->info); - sendto_one(cptr, ":%s WALLOPS :Received :%s SERVER %s from %s !?!", - me.name, parv[0], parv[1], cptr->name); - return exit_new_server(cptr, sptr, host, timestamp, "%s", acptr->info); - } - /* - * Of course we find the handshake this link was before :) - */ - else if (IsHandshake(acptr) && acptr == cptr) - break; - /* - * Here we have a server nick collision... - * We don't want to kill the link that was last /connected, - * but we neither want to kill a good (old) link. - * Therefor we kill the second youngest link. - */ - if (1) - { - struct Client* c2ptr = 0; - struct Client* c3ptr = acptr; - struct Client* ac2ptr; - struct Client* ac3ptr; - - /* Search youngest link: */ - for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up) - if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp) - c3ptr = ac3ptr; - if (IsServer(sptr)) - { - for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up) - if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp) - c3ptr = ac3ptr; - } - if (timestamp > c3ptr->serv->timestamp) - { - c3ptr = 0; - c2ptr = acptr; /* Make sure they differ */ - } - /* Search second youngest link: */ - for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up) - if (ac2ptr != c3ptr && - ac2ptr->serv->timestamp > - (c2ptr ? c2ptr->serv->timestamp : timestamp)) - c2ptr = ac2ptr; - if (IsServer(sptr)) - { - for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up) - if (ac2ptr != c3ptr && - ac2ptr->serv->timestamp > - (c2ptr ? c2ptr->serv->timestamp : timestamp)) - c2ptr = ac2ptr; - } - if (c3ptr && timestamp > (c2ptr ? c2ptr->serv->timestamp : timestamp)) - c2ptr = 0; - /* If timestamps are equal, decide which link to break - * by name. - */ - if ((c2ptr ? c2ptr->serv->timestamp : timestamp) == - (c3ptr ? c3ptr->serv->timestamp : timestamp)) - { - char* n2; - char* n2up; - char* n3; - char* n3up; - if (c2ptr) - { - n2 = c2ptr->name; - n2up = MyConnect(c2ptr) ? me.name : c2ptr->serv->up->name; - } - else - { - n2 = host; - n2up = IsServer(sptr) ? sptr->name : me.name; - } - if (c3ptr) - { - n3 = c3ptr->name; - n3up = MyConnect(c3ptr) ? me.name : c3ptr->serv->up->name; - } - else - { - n3 = host; - n3up = IsServer(sptr) ? sptr->name : me.name; - } - if (strcmp(n2, n2up) > 0) - n2 = n2up; - if (strcmp(n3, n3up) > 0) - n3 = n3up; - if (strcmp(n3, n2) > 0) - { - ac2ptr = c2ptr; - c2ptr = c3ptr; - c3ptr = ac2ptr; - } - } - /* Now squit the second youngest link: */ - if (!c2ptr) - return exit_new_server(cptr, sptr, host, timestamp, - "server %s already exists and is %ld seconds younger.", - host, (long)acptr->serv->timestamp - (long)timestamp); - else if (c2ptr->from == cptr || IsServer(sptr)) - { - struct Client *killedptrfrom = c2ptr->from; - if (active_lh_line) - { - /* - * If the L: or H: line also gets rid of this link, - * we sent just one squit. - */ - if (LHcptr && a_kills_b_too(LHcptr, c2ptr)) - break; - /* - * If breaking the loop here solves the L: or H: - * line problem, we don't squit that. - */ - if (c2ptr->from == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr))) - active_lh_line = 0; - else - { - /* - * If we still have a L: or H: line problem, - * we prefer to squit the new server, solving - * loop and L:/H: line problem with only one squit. - */ - LHcptr = 0; - break; - } - } - /* - * If the new server was introduced by a server that caused a - * Ghost less then 20 seconds ago, this is probably also - * a Ghost... (20 seconds is more then enough because all - * SERVER messages are at the beginning of a net.burst). --Run - */ - if (CurrentTime - cptr->serv->ghost < 20) - { - killedptrfrom = acptr->from; - if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED) - return CPTR_KILLED; - } - else if (exit_client_msg(cptr, c2ptr, &me, - "Loop <-- %s (new link is %ld seconds younger)", host, - (c3ptr ? (long)c3ptr->serv->timestamp : timestamp) - - (long)c2ptr->serv->timestamp) == CPTR_KILLED) - return CPTR_KILLED; - /* - * Did we kill the incoming server off already ? - */ - if (killedptrfrom == cptr) - return 0; - } - else - { - if (active_lh_line) - { - if (LHcptr && a_kills_b_too(LHcptr, acptr)) - break; - if (acptr->from == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr))) - active_lh_line = 0; - else - { - LHcptr = 0; - break; - } - } - /* - * We can't believe it is a lagged server message - * when it directly connects to us... - * kill the older link at the ghost, rather then - * at the second youngest link, assuming it isn't - * a REAL loop. - */ - ghost = CurrentTime; /* Mark that it caused a ghost */ - if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED) - return CPTR_KILLED; - break; - } - } - } - - if (active_lh_line) - { - if (LHcptr == 0) { - return exit_new_server(cptr, sptr, host, timestamp, - (active_lh_line == 2) ? "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)", - cptr->name, host, - lhconf ? (lhconf->name ? lhconf->name : "*") : "!"); - } - else - { - int killed = a_kills_b_too(LHcptr, sptr); - if (active_lh_line < 3) - { - if (exit_client_msg(cptr, LHcptr, &me, - (active_lh_line == 2) ? "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)", - cptr->name, host, - lhconf ? (lhconf->name ? lhconf->name : "*") : "!") == CPTR_KILLED) - return CPTR_KILLED; - } - else - { - ServerStats->is_ref++; - if (exit_client(cptr, LHcptr, &me, "I'm a leaf") == CPTR_KILLED) - return CPTR_KILLED; - } - /* - * Did we kill the incoming server off already ? - */ - if (killed) - return 0; - } - } - - if (IsServer(cptr)) - { - /* - * 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... - */ - - acptr = make_client(cptr, STAT_SERVER); - make_server(acptr); - acptr->serv->prot = prot; - acptr->serv->timestamp = timestamp; - acptr->hopcount = hop; - ircd_strncpy(acptr->name, host, HOSTLEN); - ircd_strncpy(acptr->info, info, REALLEN); - acptr->serv->up = sptr; - acptr->serv->updown = add_dlink(&sptr->serv->down, acptr); - /* Use cptr, because we do protocol 9 -> 10 translation - for numeric nicks ! */ - SetServerYXX(cptr, acptr, parv[6]); - - Count_newremoteserver(UserStats); - if (Protocol(acptr) < 10) - acptr->flags |= FLAGS_TS8; - add_client_to_list(acptr); - hAddClient(acptr); - if (*parv[5] == 'J') - { - SetBurst(acptr); - sendto_op_mask(SNO_NETWORK, "Net junction: %s %s", - sptr->name, acptr->name); - SetJunction(acptr); - } - /* - * Old sendto_serv_but_one() call removed because we now need to send - * different names to different servers (domain name matching). - */ - for (i = 0; i <= HighestFd; i++) - { - if (!(bcptr = LocalClientArray[i]) || !IsServer(bcptr) || - bcptr == cptr || IsMe(bcptr)) - continue; - if (0 == match(me.name, acptr->name)) - continue; - sendto_one(bcptr, "%s " TOK_SERVER " %s %d 0 %s %s %s%s 0 :%s", - NumServ(sptr), acptr->name, hop + 1, parv[4], parv[5], - NumServCap(acptr), acptr->info); - } - return 0; - } - - if (IsUnknown(cptr) || IsHandshake(cptr)) - { - make_server(cptr); - cptr->serv->timestamp = timestamp; - cptr->serv->prot = prot; - cptr->serv->ghost = ghost; - SetServerYXX(cptr, cptr, parv[6]); - if (start_timestamp > OLDEST_TS) - { -#ifndef RELIABLE_CLOCK -#ifdef TESTNET - sendto_ops("Debug: my start time: " TIME_T_FMT " ; others start time: " - TIME_T_FMT, me.serv->timestamp, start_timestamp); - sendto_ops("Debug: receive time: " TIME_T_FMT " ; received timestamp: " - TIME_T_FMT " ; difference %ld", - recv_time, timestamp, timestamp - recv_time); -#endif - if (start_timestamp < me.serv->timestamp) - { - sendto_ops("got earlier start time: " TIME_T_FMT " < " TIME_T_FMT, - start_timestamp, me.serv->timestamp); - me.serv->timestamp = start_timestamp; - TSoffset += timestamp - recv_time; - sendto_ops("clock adjusted by adding %d", (int)(timestamp - recv_time)); - } - else if ((start_timestamp > me.serv->timestamp) && IsUnknown(cptr)) - cptr->serv->timestamp = TStime(); - - else if (timestamp != recv_time) - { - /* - * Equal start times, we have a collision. Let the connected-to server - * decide. This assumes leafs issue more than half of the connection - * attempts. - */ - if (IsUnknown(cptr)) - cptr->serv->timestamp = TStime(); - else if (IsHandshake(cptr)) - { - sendto_ops("clock adjusted by adding %d", - (int)(timestamp - recv_time)); - TSoffset += timestamp - recv_time; - } - } -#else /* RELIABLE CLOCK IS TRUE, we _always_ use our own clock */ - if (start_timestamp < me.serv->timestamp) - me.serv->timestamp = start_timestamp; - if (IsUnknown(cptr)) - cptr->serv->timestamp = TStime(); -#endif - } - - ret = server_estab(cptr, aconf); - } - else - ret = 0; -#ifdef RELIABLE_CLOCK - if (abs(cptr->serv->timestamp - recv_time) > 30) - { - sendto_ops("Connected to a net with a timestamp-clock" - " difference of " STIME_T_FMT " seconds! Used SETTIME to correct" - " this.", timestamp - recv_time); - sendto_one(cptr, ":%s SETTIME " TIME_T_FMT " :%s", - me.name, TStime(), me.name); + if (feature_bool(FEAT_RELIABLE_CLOCK) && + abs(cli_serv(cptr)->timestamp - recv_time) > 30) { + sendto_opmask_butone(0, SNO_OLDSNO, "Connected to a net with a " + "timestamp-clock difference of %Td seconds! " + "Used SETTIME to correct this.", + timestamp - recv_time); + sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%Tu :%s", TStime(), + cli_name(&me)); } -#endif return ret; } - -#if 0 -/* - * m_server +/** Handle a SERVER message from another server. * - * parv[0] = sender prefix - * parv[1] = servername - * parv[2] = hopcount - * parv[3] = start timestamp - * parv[4] = link timestamp - * parv[5] = major protocol version: P09/P10 - * parv[parc-1] = serverinfo - * If cptr is P10: - * parv[6] = "YMM", where 'Y' is the server numeric and "MM" is the - * numeric nick mask of this server. - * parv[7] = 0 (not used yet, mandatory unsigned int after u2.10.06) + * \a parv has the following elements: + * \li \a parv[1] is the server name + * \li \a parv[2] is the hop count to the server + * \li \a parv[3] is the start timestamp for the server + * \li \a parv[4] is the link timestamp + * \li \a parv[5] is the protocol version (P10 or J10) + * \li \a parv[6] is the numnick mask for the server + * \li \a parv[7] is a string of flags like +hs to mark hubs and services + * \li \a parv[\a parc - 1] is the server description + * + * See @ref m_functions for discussion of the arguments. + * @param[in] cptr Client that sent us the message. + * @param[in] sptr Original source of message. + * @param[in] parc Number of arguments. + * @param[in] parv Argument vector. */ -int m_server(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) +int ms_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - char* ch; int i; - char info[REALLEN + 1]; char* host; struct Client* acptr; struct Client* bcptr; - struct Client* LHcptr = 0; - struct ConfItem* aconf = 0; - struct ConfItem* cconf; - struct ConfItem* lhconf = 0; int hop; int ret; - int active_lh_line = 0; unsigned short prot; time_t start_timestamp; - time_t timestamp = 0; - time_t recv_time; - time_t ghost = 0; + time_t timestamp; - if (IsUser(cptr)) + if (parc < 8) { - sendto_one(cptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]); - return 0; + return need_more_params(sptr, "SERVER"); + return exit_client(cptr, cptr, &me, "Need more parameters"); + } + host = clean_servername(parv[1]); + if (!host) + { + sendto_opmask_butone(0, SNO_OLDSNO, "Bogus server name (%s) from %s", + host, cli_name(cptr)); + return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host); } - if (IsUserPort(cptr)) - return exit_client_msg(cptr, cptr, &me, - "You cannot connect a server to a user port; connect to %s port %u", - me.name, server_port); - - recv_time = TStime(); - info[0] = '\0'; - if (parc < 7) - { - return need_more_params(sptr, "SERVER"); - return exit_client(cptr, cptr, &me, "Need more parameters"); - } - host = parv[1]; /* * Detect protocol */ - if (strlen(parv[5]) != 3 || (parv[5][0] != 'P' && parv[5][0] != 'J')) - return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]); - - if (!IsServer(cptr)) /* Don't allow silently connecting a server */ - *parv[5] = 'J'; - - prot = atoi(parv[5] + 1); - if (prot > atoi(MAJOR_PROTOCOL)) - prot = atoi(MAJOR_PROTOCOL); - /* - * Because the previous test is only in 2.10, the following is needed - * till all servers are 2.10: - */ - if (IsServer(cptr) && prot > Protocol(cptr)) - prot = Protocol(cptr); hop = atoi(parv[2]); start_timestamp = atoi(parv[3]); timestamp = atoi(parv[4]); - Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age " TIME_T_FMT " (" - TIME_T_FMT ")", host, parv[4], start_timestamp, me.serv->timestamp)); - if ((timestamp < OLDEST_TS || (hop == 1 && start_timestamp < OLDEST_TS))) - { - return exit_client_msg(cptr, sptr, &me, - "Bogus timestamps (%s %s)", parv[3], parv[4]); - } - ircd_strncpy(info, parv[parc - 1], REALLEN); - info[REALLEN] = '\0'; - if (prot < atoi(MINOR_PROTOCOL)) { - sendto_ops("Got incompatible protocol version (%s) from %s", - parv[5], cptr->name); + prot = parse_protocol(parv[5]); + if (!prot) + return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]); + else if (prot < atoi(MINOR_PROTOCOL)) return exit_new_server(cptr, sptr, host, timestamp, - "Incompatible protocol: %s", parv[5]); - } - /* - * Check for "FRENCH " infection ;-) (actually this should - * be replaced with routine to check the hostname syntax in - * general). [ This check is still needed, even after the parse - * is fixed, because someone can send "SERVER :foo bar " ]. - * Also, changed to check other "difficult" characters, now - * that parse lets all through... --msa - */ - if (strlen(host) > HOSTLEN) - host[HOSTLEN] = '\0'; - for (ch = host; *ch; ch++) - if (*ch <= ' ' || *ch > '~') - break; - if (*ch || !strchr(host, '.')) - { - sendto_ops("Bogus server name (%s) from %s", host, cptr->name); - return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host); - } - - if (IsServer(cptr)) - { - /* - * A local server introduces a new server behind this link. - * Check if this is allowed according L:, H: and Q: lines. - */ - if (info[0] == '\0') - return exit_client_msg(cptr, cptr, &me, - "No server info specified for %s", host); - /* - * See if the newly found server is behind a guaranteed - * leaf (L-line). If so, close the link. - */ - if ((lhconf = find_conf_byhost(cptr->confs, cptr->name, CONF_LEAF)) && - (!lhconf->port || (hop > lhconf->port))) - { - /* - * L: lines normally come in pairs, here we try to - * make sure that the oldest link is squitted, not - * both. - */ - active_lh_line = 1; - if (timestamp <= cptr->serv->timestamp) - LHcptr = 0; /* Kill incoming server */ - else - LHcptr = cptr; /* Squit ourselfs */ - } - else if (!(lhconf = find_conf_byname(cptr->confs, cptr->name, CONF_HUB)) || - (lhconf->port && (hop > lhconf->port))) - { - struct Client *ac3ptr; - active_lh_line = 2; - /* Look for net junction causing this: */ - LHcptr = 0; /* incoming server */ - if (*parv[5] != 'J') { - for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up) { - if (IsJunction(ac3ptr)) { - LHcptr = ac3ptr; - break; - } - } - } - } - } - - if (IsUnknown(cptr) || IsHandshake(cptr)) - { - const char* encr; - - /* - * A local link that is still in undefined state wants - * to be a SERVER. Check if this is allowed and change - * status accordingly... - */ - /* - * If there is more then one server on the same machine - * that we try to connect to, it could be that the /CONNECT - * caused this connect to be put at the wrong place - * in the hashtable. --Run - * Same thing for Unknown connections that first send NICK. - * --Xorath - * Better check if the two strings are (caseless) identical - * and not mess with hash internals. - * --Nemesi - */ - if ((!(EmptyString(cptr->name))) - && (IsUnknown(cptr) || IsHandshake(cptr)) - && 0 != ircd_strcmp(cptr->name, host)) - hChangeClient(cptr, host); - ircd_strncpy(cptr->name, host, HOSTLEN); - ircd_strncpy(cptr->info, info[0] ? info : me.name, REALLEN); - cptr->hopcount = hop; - - /* check connection rules */ - for (cconf = GlobalConfList; cconf; cconf = cconf->next) { - if ((cconf->status == CONF_CRULEALL) && (match(cconf->host, host) == 0)) { - if (crule_eval(cconf->passwd)) - { - ServerStats->is_ref++; - sendto_ops("Refused connection from %s.", cptr->name); - return exit_client(cptr, cptr, &me, "Disallowed by connection rule"); - } - } - } - if (conf_check_server(cptr)) { - ++ServerStats->is_ref; - sendto_ops("Received unauthorized connection from %s.", cptr->name); - return exit_client(cptr, cptr, &me, "No C/N conf lines"); - } + "Incompatible protocol: %s", parv[5]); - host = cptr->name; + Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)", + host, parv[4], start_timestamp, cli_serv(&me)->timestamp)); - update_load(); + if (timestamp < OLDEST_TS) + return exit_client_msg(cptr, sptr, &me, + "Bogus timestamps (%s %s)", parv[3], parv[4]); - if (!(aconf = find_conf_byname(cptr->confs, host, CONF_SERVER))) { - ++ServerStats->is_ref; -#ifndef GODMODE - sendto_ops("Access denied. No conf line for server %s", cptr->name); - return exit_client_msg(cptr, cptr, &me, - "Access denied. No conf line for server %s", cptr->name); -#else /* GODMODE */ - sendto_ops("General C/N: line active: No line for server %s", cptr->name); - aconf = - find_conf_byname(cptr->confs, "general.undernet.org", CONF_SERVER); - if (!aconf) { - sendto_ops("Neither C/N lines for server %s nor " - "\"general.undernet.org\"", cptr->name); - return exit_client_msg(cptr, cptr, &me, - "No C/N lines for server %s", cptr->name); - } -#endif /* GODMODE */ - } -#ifdef CRYPT_LINK_PASSWORD - /* passwd may be NULL. Head it off at the pass... */ - if (*cptr->passwd) - { - char salt[3]; + if (parv[parc - 1][0] == '\0') + return exit_client_msg(cptr, cptr, &me, + "No server info specified for %s", host); - salt[0] = aconf->passwd[0]; - salt[1] = aconf->passwd[1]; - salt[2] = '\0'; - encr = ircd_crypt(cptr->passwd, salt); - } - else - encr = ""; -#else - encr = cptr->passwd; -#endif /* CRYPT_LINK_PASSWORD */ -#ifndef GODMODE - if (*aconf->passwd && !!strcmp(aconf->passwd, encr)) { - ++ServerStats->is_ref; - sendto_ops("Access denied (passwd mismatch) %s", cptr->name); - return exit_client_msg(cptr, cptr, &me, - "No Access (passwd mismatch) %s", cptr->name); - } -#endif /* not GODMODE */ - memset(cptr->passwd, 0, sizeof(cptr->passwd)); - -#ifndef HUB - for (i = 0; i <= HighestFd; i++) - if (LocalClientArray[i] && IsServer(LocalClientArray[i])) { - active_lh_line = 3; - LHcptr = 0; - break; - } -#endif - } + ret = check_loop_and_lh(cptr, sptr, NULL, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, parv[5][0] == 'J'); + if (ret != 1) + return ret; /* - * We want to find IsConnecting() and IsHandshake() too, - * use FindClient(). - * The second finds collisions with numeric representation of existing - * servers - these shouldn't happen anymore when all upgraded to 2.10. - * -- Run + * 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... */ - while ((acptr = FindClient(host)) || - (parc > 7 && (acptr = FindNServer(parv[6])))) - { - /* - * This link is trying feed me a server that I already have - * access through another path - * - * Do not allow Uworld to do this. - * Do not allow servers that are juped. - * Do not allow servers that have older link timestamps - * then this try. - * Do not allow servers that use the same numeric as an existing - * server, but have a different name. - * - * If my ircd.conf sucks, I can try to connect to myself: - */ - if (acptr == &me) - return exit_client_msg(cptr, cptr, &me, - "nick collision with me (%s)", host); - /* - * Detect wrong numeric. - */ - if (0 != ircd_strcmp(acptr->name, host)) - { - sendto_serv_butone(cptr, - ":%s WALLOPS :SERVER Numeric Collision: %s != %s", - me.name, acptr->name, host); - return exit_client_msg(cptr, cptr, &me, - "NUMERIC collision between %s and %s." - " Is your server numeric correct ?", host, acptr->name); - } - /* - * Kill our try, if we had one. - */ - if (IsConnecting(acptr)) - { - if (!active_lh_line && exit_client(cptr, acptr, &me, - "Just connected via another link") == CPTR_KILLED) - return CPTR_KILLED; - /* - * We can have only ONE 'IsConnecting', 'IsHandshake' or - * 'IsServer', because new 'IsConnecting's are refused to - * the same server if we already had it. - */ - break; - } - /* - * Avoid other nick collisions... - * This is a doubtfull test though, what else would it be - * when it has a server.name ? - */ - else if (!IsServer(acptr) && !IsHandshake(acptr)) - return exit_client_msg(cptr, cptr, &me, - "Nickname %s already exists!", host); - /* - * Our new server might be a juped server, - * or someone trying abuse a second Uworld: - */ - else if (IsServer(acptr) && (0 == ircd_strncmp(acptr->info, "JUPE", 4) || - find_conf_byhost(cptr->confs, acptr->name, CONF_UWORLD))) - { - if (!IsServer(sptr)) - return exit_client(cptr, sptr, &me, acptr->info); - sendto_one(cptr, ":%s WALLOPS :Received :%s SERVER %s from %s !?!", - me.name, parv[0], parv[1], cptr->name); - return exit_new_server(cptr, sptr, host, timestamp, "%s", acptr->info); - } - /* - * Of course we find the handshake this link was before :) - */ - else if (IsHandshake(acptr) && acptr == cptr) - break; - /* - * Here we have a server nick collision... - * We don't want to kill the link that was last /connected, - * but we neither want to kill a good (old) link. - * Therefor we kill the second youngest link. - */ - if (1) - { - struct Client* c2ptr = 0; - struct Client* c3ptr = acptr; - struct Client* ac2ptr; - struct Client* ac3ptr; - /* Search youngest link: */ - for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up) - if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp) - c3ptr = ac3ptr; - if (IsServer(sptr)) - { - for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up) - if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp) - c3ptr = ac3ptr; - } - if (timestamp > c3ptr->serv->timestamp) - { - c3ptr = 0; - c2ptr = acptr; /* Make sure they differ */ - } - /* Search second youngest link: */ - for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up) - if (ac2ptr != c3ptr && - ac2ptr->serv->timestamp > - (c2ptr ? c2ptr->serv->timestamp : timestamp)) - c2ptr = ac2ptr; - if (IsServer(sptr)) - { - for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up) - if (ac2ptr != c3ptr && - ac2ptr->serv->timestamp > - (c2ptr ? c2ptr->serv->timestamp : timestamp)) - c2ptr = ac2ptr; - } - if (c3ptr && timestamp > (c2ptr ? c2ptr->serv->timestamp : timestamp)) - c2ptr = 0; - /* If timestamps are equal, decide which link to break - * by name. - */ - if ((c2ptr ? c2ptr->serv->timestamp : timestamp) == - (c3ptr ? c3ptr->serv->timestamp : timestamp)) - { - char* n2; - char* n2up; - char* n3; - char* n3up; - if (c2ptr) - { - n2 = c2ptr->name; - n2up = MyConnect(c2ptr) ? me.name : c2ptr->serv->up->name; - } - else - { - n2 = host; - n2up = IsServer(sptr) ? sptr->name : me.name; - } - if (c3ptr) - { - n3 = c3ptr->name; - n3up = MyConnect(c3ptr) ? me.name : c3ptr->serv->up->name; - } - else - { - n3 = host; - n3up = IsServer(sptr) ? sptr->name : me.name; - } - if (strcmp(n2, n2up) > 0) - n2 = n2up; - if (strcmp(n3, n3up) > 0) - n3 = n3up; - if (strcmp(n3, n2) > 0) - { - ac2ptr = c2ptr; - c2ptr = c3ptr; - c3ptr = ac2ptr; - } - } - /* Now squit the second youngest link: */ - if (!c2ptr) - return exit_new_server(cptr, sptr, host, timestamp, - "server %s already exists and is %ld seconds younger.", - host, (long)acptr->serv->timestamp - (long)timestamp); - else if (c2ptr->from == cptr || IsServer(sptr)) - { - struct Client *killedptrfrom = c2ptr->from; - if (active_lh_line) - { - /* - * If the L: or H: line also gets rid of this link, - * we sent just one squit. - */ - if (LHcptr && a_kills_b_too(LHcptr, c2ptr)) - break; - /* - * If breaking the loop here solves the L: or H: - * line problem, we don't squit that. - */ - if (c2ptr->from == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr))) - active_lh_line = 0; - else - { - /* - * If we still have a L: or H: line problem, - * we prefer to squit the new server, solving - * loop and L:/H: line problem with only one squit. - */ - LHcptr = 0; - break; - } - } - /* - * If the new server was introduced by a server that caused a - * Ghost less then 20 seconds ago, this is probably also - * a Ghost... (20 seconds is more then enough because all - * SERVER messages are at the beginning of a net.burst). --Run - */ - if (CurrentTime - cptr->serv->ghost < 20) - { - killedptrfrom = acptr->from; - if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED) - return CPTR_KILLED; - } - else if (exit_client_msg(cptr, c2ptr, &me, - "Loop <-- %s (new link is %ld seconds younger)", host, - (c3ptr ? (long)c3ptr->serv->timestamp : timestamp) - - (long)c2ptr->serv->timestamp) == CPTR_KILLED) - return CPTR_KILLED; - /* - * Did we kill the incoming server off already ? - */ - if (killedptrfrom == cptr) - return 0; - } - else - { - if (active_lh_line) - { - if (LHcptr && a_kills_b_too(LHcptr, acptr)) - break; - if (acptr->from == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr))) - active_lh_line = 0; - else - { - LHcptr = 0; - break; - } - } - /* - * We can't believe it is a lagged server message - * when it directly connects to us... - * kill the older link at the ghost, rather then - * at the second youngest link, assuming it isn't - * a REAL loop. - */ - ghost = CurrentTime; /* Mark that it caused a ghost */ - if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED) - return CPTR_KILLED; - break; - } - } - } - - if (active_lh_line) - { - if (LHcptr == 0) { - return exit_new_server(cptr, sptr, host, timestamp, - (active_lh_line == 2) ? "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)", - cptr->name, host, - lhconf ? (lhconf->name ? lhconf->name : "*") : "!"); - } - else - { - int killed = a_kills_b_too(LHcptr, sptr); - if (active_lh_line < 3) - { - if (exit_client_msg(cptr, LHcptr, &me, - (active_lh_line == 2) ? "Non-Hub link %s <- %s(%s)" : "Leaf-only link %s <- %s(%s)", - cptr->name, host, - lhconf ? (lhconf->name ? lhconf->name : "*") : "!") == CPTR_KILLED) - return CPTR_KILLED; - } - else - { - ServerStats->is_ref++; - if (exit_client(cptr, LHcptr, &me, "I'm a leaf") == CPTR_KILLED) - return CPTR_KILLED; - } - /* - * Did we kill the incoming server off already ? - */ - if (killed) - return 0; - } - } - - if (IsServer(cptr)) - { - /* - * 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... - */ - - acptr = make_client(cptr, STAT_SERVER); - make_server(acptr); - acptr->serv->prot = prot; - acptr->serv->timestamp = timestamp; - acptr->hopcount = hop; - ircd_strncpy(acptr->name, host, HOSTLEN); - ircd_strncpy(acptr->info, info, REALLEN); - acptr->serv->up = sptr; - acptr->serv->updown = add_dlink(&sptr->serv->down, acptr); - /* Use cptr, because we do protocol 9 -> 10 translation - for numeric nicks ! */ - SetServerYXX(cptr, acptr, parv[6]); - - Count_newremoteserver(UserStats); - if (Protocol(acptr) < 10) - acptr->flags |= FLAGS_TS8; - add_client_to_list(acptr); - hAddClient(acptr); - if (*parv[5] == 'J') - { - SetBurst(acptr); - sendto_op_mask(SNO_NETWORK, "Net junction: %s %s", - sptr->name, acptr->name); - SetJunction(acptr); - } - /* - * Old sendto_serv_but_one() call removed because we now need to send - * different names to different servers (domain name matching). - */ - for (i = 0; i <= HighestFd; i++) - { - if (!(bcptr = LocalClientArray[i]) || !IsServer(bcptr) || - bcptr == cptr || IsMe(bcptr)) - continue; - if (0 == match(me.name, acptr->name)) - continue; - sendto_one(bcptr, "%s " TOK_SERVER " %s %d 0 %s %s %s%s 0 :%s", - NumServ(sptr), acptr->name, hop + 1, parv[4], parv[5], - NumServCap(acptr), acptr->info); - } - return 0; - } - - if (IsUnknown(cptr) || IsHandshake(cptr)) + acptr = make_client(cptr, STAT_SERVER); + make_server(acptr); + cli_serv(acptr)->prot = prot; + cli_serv(acptr)->timestamp = timestamp; + cli_hopcount(acptr) = hop; + ircd_strncpy(cli_name(acptr), host, HOSTLEN); + ircd_strncpy(cli_info(acptr), parv[parc-1], REALLEN); + cli_serv(acptr)->up = sptr; + cli_serv(acptr)->updown = add_dlink(&(cli_serv(sptr))->down, acptr); + /* Use cptr, because we do protocol 9 -> 10 translation + for numeric nicks ! */ + SetServerYXX(cptr, acptr, parv[6]); + + /* Attach any necessary UWorld config items. */ + attach_confs_byhost(cptr, host, CONF_UWORLD); + + if (*parv[7] == '+') + set_server_flags(acptr, parv[7] + 1); + + Count_newremoteserver(UserStats); + if (Protocol(acptr) < 10) + SetFlag(acptr, FLAG_TS8); + add_client_to_list(acptr); + hAddClient(acptr); + if (*parv[5] == 'J') { - make_server(cptr); - cptr->serv->timestamp = timestamp; - cptr->serv->prot = prot; - cptr->serv->ghost = ghost; - SetServerYXX(cptr, cptr, parv[6]); - if (start_timestamp > OLDEST_TS) - { -#ifndef RELIABLE_CLOCK -#ifdef TESTNET - sendto_ops("Debug: my start time: " TIME_T_FMT " ; others start time: " - TIME_T_FMT, me.serv->timestamp, start_timestamp); - sendto_ops("Debug: receive time: " TIME_T_FMT " ; received timestamp: " - TIME_T_FMT " ; difference %ld", - recv_time, timestamp, timestamp - recv_time); -#endif - if (start_timestamp < me.serv->timestamp) - { - sendto_ops("got earlier start time: " TIME_T_FMT " < " TIME_T_FMT, - start_timestamp, me.serv->timestamp); - me.serv->timestamp = start_timestamp; - TSoffset += timestamp - recv_time; - sendto_ops("clock adjusted by adding %d", (int)(timestamp - recv_time)); - } - else if ((start_timestamp > me.serv->timestamp) && IsUnknown(cptr)) - cptr->serv->timestamp = TStime(); - - else if (timestamp != recv_time) - { - /* - * Equal start times, we have a collision. Let the connected-to server - * decide. This assumes leafs issue more than half of the connection - * attempts. - */ - if (IsUnknown(cptr)) - cptr->serv->timestamp = TStime(); - else if (IsHandshake(cptr)) - { - sendto_ops("clock adjusted by adding %d", - (int)(timestamp - recv_time)); - TSoffset += timestamp - recv_time; - } - } -#else /* RELIABLE CLOCK IS TRUE, we _always_ use our own clock */ - if (start_timestamp < me.serv->timestamp) - me.serv->timestamp = start_timestamp; - if (IsUnknown(cptr)) - cptr->serv->timestamp = TStime(); -#endif - } - - ret = server_estab(cptr, aconf); + SetBurst(acptr); + SetJunction(acptr); + for (bcptr = cli_serv(acptr)->up; !IsMe(bcptr); bcptr = cli_serv(bcptr)->up) + if (IsBurstOrBurstAck(bcptr)) + break; + if (IsMe(bcptr)) + sendto_opmask_butone(0, SNO_NETWORK, "Net junction: %s %s", + cli_name(sptr), cli_name(acptr)); } - else - ret = 0; -#ifdef RELIABLE_CLOCK - if (abs(cptr->serv->timestamp - recv_time) > 30) + /* + * Old sendto_serv_but_one() call removed because we now need to send + * different names to different servers (domain name matching). + */ + for (i = 0; i <= HighestFd; i++) { - sendto_ops("Connected to a net with a timestamp-clock" - " difference of " STIME_T_FMT " seconds! Used SETTIME to correct" - " this.", timestamp - recv_time); - sendto_one(cptr, ":%s SETTIME " TIME_T_FMT " :%s", - me.name, TStime(), me.name); - } -#endif - - return ret; + if (!(bcptr = LocalClientArray[i]) || !IsServer(bcptr) || + bcptr == cptr || IsMe(bcptr)) + continue; + if (0 == match(cli_name(&me), cli_name(acptr))) + continue; + sendcmdto_one(sptr, CMD_SERVER, bcptr, "%s %d 0 %s %s %s%s +%s%s%s :%s", + cli_name(acptr), hop + 1, parv[4], parv[5], + NumServCap(acptr), IsHub(acptr) ? "h" : "", + IsService(acptr) ? "s" : "", IsIPv6(acptr) ? "6" : "", + cli_info(acptr)); + } + return 0; } -#endif /* 0 */ -