"Fossies" - the Fresh Open Source Software Archive

Member "bahamut-2.1.5/src/m_server.c" (28 May 2020, 31630 Bytes) of package /linux/privat/bahamut-2.1.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "m_server.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.1.4_vs_2.1.5.

    1 /* Bahamut IRCd, src/m_server.c
    2  * Copyright (c) 2004, Aaron Wiebe and the Bahamut Team
    3  *
    4  *   This program is free software; you can redistribute it and/or modify
    5  *   it under the terms of the GNU General Public License as published by
    6  *   the Free Software Foundation; either version 1, or (at your option)
    7  *   any later version.
    8  *
    9  *   This program is distributed in the hope that it will be useful,
   10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  *   GNU General Public License for more details.
   13  *
   14  *   You should have received a copy of the GNU General Public License
   15  *   along with this program; if not, write to the Free Software
   16  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   17  */
   18 
   19 #include "struct.h"
   20 #include "common.h"
   21 #include "sys.h"
   22 #include "numeric.h"
   23 #include "h.h"
   24 #include "dh.h"
   25 #include "userban.h"
   26 #include "zlink.h"
   27 #include "throttle.h"
   28 #include "clones.h"
   29 
   30 /* externally defined functions */
   31 
   32 extern void fakelinkserver_update(char *, char *);
   33 extern void fakeserver_sendserver(aClient *);
   34 extern void fakelusers_sendlock(aClient *);
   35 extern void reset_sock_opts(int, int);
   36 extern void spamfilter_sendserver(aClient *acptr);
   37 extern int user_modes[];
   38 extern int uhm_type;
   39 extern int uhm_umodeh;
   40 
   41 /* internal functions */
   42 
   43 static void sendnick_TS(aClient *cptr, aClient *acptr)
   44 {
   45     ServicesTag *servicestag;
   46     static char ubuf[54];
   47     int *s, flag, i;
   48 
   49     if (IsPerson(acptr))
   50     {
   51         send_umode(NULL, acptr, 0, SEND_UMODES, ubuf, sizeof(ubuf));
   52         if (!*ubuf)     /* trivial optimization - Dianora */
   53         {
   54             ubuf[0] = '+';
   55             ubuf[1] = '\0';
   56         }
   57     if (IsNickIPStr(cptr))
   58     {
   59         sendto_one(cptr, "NICK %s %d %ld %s %s %s %s %lu %s :%s",
   60                acptr->name, acptr->hopcount + 1, acptr->tsinfo, ubuf,
   61                acptr->user->username, acptr->user->host,
   62                acptr->user->server, acptr->user->servicestamp,
   63                cipntoa(acptr), acptr->info);
   64     }
   65     else
   66     {
   67         sendto_one(cptr, "NICK %s %d %ld %s %s %s %s %lu %u :%s",
   68                acptr->name, acptr->hopcount + 1, acptr->tsinfo, ubuf,
   69                acptr->user->username, acptr->user->host,
   70                acptr->user->server, acptr->user->servicestamp,
   71                (acptr->ip_family == AF_INET) ?
   72                htonl(acptr->ip.ip4.s_addr) : 1, acptr->info);
   73     }
   74         for(servicestag = acptr->user->servicestag; servicestag; servicestag = servicestag->next)
   75         {
   76             ubuf[0] = '+';
   77             i = 1;
   78             for (s = user_modes; (flag = *s); s += 2)
   79                 if(servicestag->umode & flag)
   80                 {
   81                    ubuf[i++] = *(s + 1);
   82                 }
   83             ubuf[i++] = '\0';
   84             sendto_one(cptr, "SVSTAG %s %ld %d %s :%s", acptr->name, acptr->tsinfo, servicestag->raw,
   85                        ubuf, servicestag->tag);
   86         }
   87 #ifdef USER_HOSTMASKING
   88         if(acptr->flags & FLAGS_SPOOFED)
   89             sendto_one(cptr, "SVSHOST %s %s", acptr->name, acptr->user->mhost);
   90 #endif
   91     }
   92 }
   93 
   94 static int
   95 do_server_estab(aClient *cptr)
   96 {
   97     aClient *acptr;
   98     aConnect *aconn;
   99     aChannel *chptr;
  100     int i;
  101     /* "refresh" inpath with host  */
  102     char *inpath = get_client_name(cptr, HIDEME);
  103 
  104     SetServer(cptr);
  105 
  106     Count.unknown--;
  107     Count.server++;
  108     Count.myserver++;
  109 
  110     if(IsZipCapable(cptr) && DoZipThis(cptr))
  111     {
  112         sendto_one(cptr, "SVINFO ZIP");
  113         SetZipOut(cptr);
  114         cptr->serv->zip_out = zip_create_output_session();
  115     }
  116 
  117 #ifdef MAXBUFFERS
  118     /* let's try to bump up server sock_opts... -Taner */
  119     reset_sock_opts(cptr->fd, 1);
  120 #endif
  121 
  122     /* adds to server list */
  123     add_to_list(&server_list, cptr);
  124 
  125     set_effective_class(cptr);
  126 
  127     /* Check one more time for good measure... is it there? */
  128     if ((acptr = find_name(cptr->name, NULL)))
  129     {
  130         char        nbuf[HOSTLEN * 2 + USERLEN + 5];
  131         aClient *bcptr;
  132 
  133         /*
  134          * While negotiating stuff, another copy of this server appeared.
  135          *
  136          * Rather than KILL the link which introduced it, KILL the
  137          * youngest of the two links. -avalon
  138          */
  139 
  140         bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr :
  141             acptr->from;
  142         sendto_one(bcptr, "ERROR :Server %s already exists", cptr->name);
  143         if (bcptr == cptr)
  144         {
  145             sendto_gnotice("from %s: Link %s cancelled, server %s already "
  146                            "exists (final phase)", me.name,
  147                            get_client_name(bcptr, HIDEME), cptr->name);
  148             sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, "
  149                                 "server %s already exists (final phase)",
  150                                 me.name, get_client_name(bcptr, HIDEME),
  151                                 cptr->name);
  152             return exit_client(bcptr, bcptr, &me,
  153                                "Server Exists (final phase)");
  154         }
  155         /* inform all those who care (set +n) -epi */
  156 
  157         strcpy(nbuf, get_client_name(bcptr, HIDEME));
  158         sendto_gnotice("from %s: Link %s cancelled, server %s reintroduced "
  159                        "by %s (final phase)", me.name, nbuf, cptr->name,
  160                        get_client_name(cptr, HIDEME));
  161         sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s "
  162                            "reintroduced by %s (final phase)", me.name, nbuf,
  163                            cptr->name, get_client_name(cptr, HIDEME));
  164         exit_client(bcptr, bcptr, &me, "Server Exists (final phase)");
  165     }
  166 
  167     /* error, error, error! if a server is U:'d, and it connects to us,
  168      * we need to figure that out! So, do it here. - lucas
  169      */
  170 
  171     if (find_aUserver(cptr->name))
  172     {
  173         Count.myulined++;
  174         cptr->flags |= FLAGS_ULINE;
  175 
  176         /* If the server has special u:line flags, let's set them.. */
  177         cptr->serv->uflags = cptr->serv->aconn->uflags;
  178     }
  179 
  180     fakelinkserver_update(cptr->name, cptr->info);
  181 
  182     sendto_gnotice("from %s: Link with %s established, states:%s%s%s%s",
  183                    me.name, inpath, ZipOut(cptr) ? " Output-compressed" : "",
  184                    RC4EncLink(cptr) ? " encrypted" : "",
  185                    IsULine(cptr) ? " ULined" : "",
  186                    DoesTS(cptr) ? " TS" : " Non-TS");
  187 
  188     /*
  189      * Notify everyone of the fact that this has just linked: the entire
  190      * network should get two of these, one explaining the link between
  191      * me->serv and the other between serv->me
  192      */
  193 
  194     sendto_serv_butone(NULL, ":%s GNOTICE :Link with %s established: %s",
  195                        me.name, inpath,
  196                        DoesTS(cptr) ? "TS link" : "Non-TS link!");
  197 
  198     add_to_client_hash_table(cptr->name, cptr);
  199 
  200     /* add it to scache */
  201 
  202     find_or_add(cptr->name);
  203 
  204     /*
  205      * Old sendto_serv_but_one() call removed because we now need to
  206      * send different names to different servers (domain name
  207      * matching) Send new server to other servers.
  208      */
  209     for (i = 0; i <= highest_fd; i++)
  210     {
  211         if (!(acptr = local[i]) || !IsServer(acptr) || acptr == cptr ||
  212             IsMe(acptr))
  213             continue;
  214         if ((aconn = acptr->serv->aconn) &&
  215             !match(my_name_for_link(me.name, aconn), cptr->name))
  216             continue;
  217         sendto_one(acptr, ":%s SERVER %s 2 :%s", me.name, cptr->name,
  218                    cptr->info);
  219     }
  220 
  221     /*
  222      * Pass on my client information to the new server
  223      *
  224      * First, pass only servers (idea is that if the link gets
  225      * cancelled beacause the server was already there, there are no
  226      * NICK's to be cancelled...). Of course, if cancellation occurs,
  227      * all this info is sent anyway, and I guess the link dies when a
  228      * read is attempted...? --msa
  229      *
  230      * Note: Link cancellation to occur at this point means that at
  231      * least two servers from my fragment are building up connection
  232      * this other fragment at the same time, it's a race condition,
  233      * not the normal way of operation...
  234      *
  235      * ALSO NOTE: using the get_client_name for server names-- see
  236      * previous *WARNING*!!! (Also, original inpath is
  237      * destroyed...)
  238      */
  239 
  240     aconn = cptr->serv->aconn;
  241     for (acptr = &me; acptr; acptr = acptr->prev)
  242     {
  243         if (acptr->from == cptr)
  244             continue;
  245         if (IsServer(acptr))
  246         {
  247             if (match(my_name_for_link(me.name, aconn), acptr->name) == 0)
  248                 continue;
  249             sendto_one(cptr, ":%s SERVER %s %d :%s",
  250                        acptr->serv->up, acptr->name,
  251                        acptr->hopcount + 1, acptr->info);
  252         }
  253     }
  254 
  255     /* send out our SQLINES and SGLINES too */
  256     send_simbans(cptr, SBAN_CHAN|SBAN_NETWORK);
  257     send_simbans(cptr, SBAN_NICK|SBAN_NETWORK);
  258     send_simbans(cptr, SBAN_GCOS|SBAN_NETWORK);
  259 
  260     /* Send out fake server list and other 'fake' stuff */
  261     fakeserver_sendserver(cptr);
  262 
  263 #ifdef SPAMFILTER
  264     /* Send the spamfilters... */
  265     spamfilter_sendserver(cptr);
  266 #endif
  267 
  268     /* Send UHM (user host-masking) type */
  269     if(confopts & FLAGS_HUB)
  270         sendto_one(cptr, "SVSUHM %d %d", uhm_type, uhm_umodeh);
  271 
  272     /* send clone list */
  273     clones_send(cptr);
  274 
  275     /* Bursts are about to start.. send a BURST */
  276     if (IsBurst(cptr))
  277         sendto_one(cptr, "BURST");
  278 
  279     /*
  280      * * Send it in the shortened format with the TS, if it's a TS
  281      * server; walk the list of channels, sending all the nicks that
  282      * haven't been sent yet for each channel, then send the channel
  283      * itself -- it's less obvious than sending all nicks first, but
  284      * on the receiving side memory will be allocated more nicely
  285      * saving a few seconds in the handling of a split -orabidoo
  286      */
  287     {
  288         chanMember       *cm;
  289         static char nickissent = 1;
  290 
  291         nickissent = 3 - nickissent;
  292         /*
  293          * flag used for each nick to check if we've sent it yet - must
  294          * be different each time and !=0, so we alternate between 1 and
  295          * 2 -orabidoo
  296          */
  297         for (chptr = channel; chptr; chptr = chptr->nextch)
  298         {
  299             for (cm = chptr->members; cm; cm = cm->next)
  300             {
  301                 acptr = cm->cptr;
  302                 if (acptr->nicksent != nickissent)
  303                 {
  304                     acptr->nicksent = nickissent;
  305                     if (acptr->from != cptr)
  306                         sendnick_TS(cptr, acptr);
  307                 }
  308             }
  309             send_channel_modes(cptr, chptr);
  310         }
  311         /* also send out those that are not on any channel */
  312         for (acptr = &me; acptr; acptr = acptr->prev)
  313             if (acptr->nicksent != nickissent)
  314             {
  315                 acptr->nicksent = nickissent;
  316                 if (acptr->from != cptr)
  317                     sendnick_TS(cptr, acptr);
  318             }
  319     }
  320 
  321     if(confopts & FLAGS_HUB)
  322         fakelusers_sendlock(cptr);
  323 
  324     if(ZipOut(cptr))
  325     {
  326         unsigned long inb, outb;
  327         double rat;
  328 
  329         zip_out_get_stats(cptr->serv->zip_out, &inb, &outb, &rat);
  330 
  331         if(inb)
  332         {
  333             sendto_gnotice("from %s: Connect burst to %s: %lu bytes normal, "
  334                            "%lu compressed (%3.2f%%)", me.name,
  335                            get_client_name(cptr, HIDEME), inb, outb, rat);
  336             sendto_serv_butone(cptr, ":%s GNOTICE :Connect burst to %s: %lu "
  337                                "bytes normal, %lu compressed (%3.2f%%)",
  338                                me.name, get_client_name(cptr, HIDEME), inb,
  339                                outb, rat);
  340         }
  341     }
  342 
  343     /* stuff a PING at the end of this burst so we can figure out when
  344        the other side has finished processing it. */
  345     cptr->flags |= FLAGS_BURST|FLAGS_PINGSENT;
  346     if (IsBurst(cptr)) cptr->flags |= FLAGS_SOBSENT;
  347     sendto_one(cptr, "PING :%s", me.name);
  348 
  349     return 0;
  350 }
  351 
  352 static int
  353 m_server_estab(aClient *cptr)
  354 {
  355     aConnect *aconn;
  356     aClient *acptr;
  357 
  358     char       *inpath, *host, *s, *encr;
  359 
  360     inpath = get_client_name(cptr, HIDEME);  /* "refresh" inpath with host  */
  361     host = cptr->name;
  362 
  363     if (!(aconn = cptr->serv->aconn))
  364     {
  365         ircstp->is_ref++;
  366         sendto_one(cptr, "ERROR :Lost Connect block");
  367         sendto_ops_lev(ADMIN_LEV, "Lost Connect block for server %s",
  368                            get_client_name(cptr, TRUE));
  369         return exit_client(cptr, cptr, cptr, "Lost Connect block");
  370     }
  371 
  372     encr = cptr->passwd;
  373     if (*aconn->apasswd && !StrEq(aconn->apasswd, encr))
  374     {
  375         ircstp->is_ref++;
  376         sendto_one(cptr, "ERROR :Wrong link password");
  377         sendto_gnotice("from %s: Link %s dropped, wrong password",
  378                        me.name, inpath);
  379         sendto_serv_butone(cptr, ":%s GNOTICE :Link %s dropped, wrong"
  380                            " password", me.name, inpath);
  381         return exit_client(cptr, cptr, cptr, "Bad Password");
  382     }
  383     memset(cptr->passwd, '\0', sizeof(cptr->passwd));
  384 
  385     if ((acptr = find_client(host, NULL)))
  386     {
  387         /* Don't complain about juped servers */
  388         if(!IsULine(acptr) || find_aUserver(acptr->name))
  389         {
  390             sendto_gnotice("from %s: Link %s dropped, server already exists",
  391                            me.name, inpath);
  392             sendto_serv_butone(cptr, ":%s GNOTICE :Link %s dropped, server already"
  393                                " exists", me.name, inpath);
  394         }
  395         return exit_client(cptr, cptr, cptr, "Server Exists");
  396     }
  397 
  398     if(!(confopts & FLAGS_HUB))
  399     {
  400         int i;
  401         for (i = 0; i <= highest_fd; i++)
  402             if (local[i] && IsServer(local[i]))
  403             {
  404                 ircstp->is_ref++;
  405                 sendto_one(cptr, "ERROR :I'm a leaf not a hub");
  406                 return exit_client(cptr, cptr, cptr, "I'm a leaf");
  407             }
  408     }
  409 
  410     /* aconf->port is a CAPAB field, kind-of. kludge. mm, mm. */
  411     /* no longer! this should still get better though */
  412     if((aconn->flags & CONN_ZIP))
  413         SetZipCapable(cptr);
  414     if((aconn->flags & CONN_DKEY))
  415         SetWantDKEY(cptr);
  416     if (IsUnknown(cptr))
  417     {
  418         if (aconn->cpasswd[0])
  419             sendto_one(cptr, "PASS %s :TS", aconn->cpasswd);
  420 
  421         /* Pass my info to the new server */
  422 
  423 #ifdef HAVE_ENCRYPTION_ON
  424         if(!WantDKEY(cptr))
  425             sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP "
  426                        "NICKIP NICKIPSTR TSMODE");
  427         else
  428             sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT DKEY "
  429                        "ZIP NICKIP NICKIPSTR TSMODE");
  430 #else
  431         sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP NICKIP NICKIPSTR TSMODE");
  432 #endif
  433 
  434         sendto_one(cptr, "SERVER %s 1 :%s",
  435                    my_name_for_link(me.name, aconn),
  436                    (me.info[0]) ? (me.info) : "IRCers United");
  437     }
  438     else 
  439     {
  440         s = (char *) strchr(aconn->host, '@');
  441         *s = '\0';      /* should never be NULL -- wanna bet? -Dianora */
  442 
  443         Debug((DEBUG_INFO, "Check Usernames [%s]vs[%s]", aconn->host,
  444                cptr->username));
  445         if (match(aconn->host, cptr->username))
  446         {
  447             *s = '@';
  448             ircstp->is_ref++;
  449             sendto_ops("Username mismatch [%s]v[%s] : %s",
  450                        aconn->host, cptr->username,
  451                        get_client_name(cptr, HIDEME));
  452             sendto_one(cptr, "ERROR :No Username Match");
  453             return exit_client(cptr, cptr, cptr, "Bad User");
  454         }
  455         *s = '@';
  456     }
  457 
  458     /* send routing notice, this should never happen anymore */
  459     if (!DoesTS(cptr))
  460     {
  461         sendto_gnotice("from %s: Warning: %s linked, non-TS server",
  462                        me.name, get_client_name(cptr, HIDEME));
  463         sendto_serv_butone(cptr,
  464                            ":%s GNOTICE :Warning: %s linked, non-TS server",
  465                            me.name, get_client_name(cptr, HIDEME));
  466     }
  467 
  468     sendto_one(cptr, "SVINFO %d %d 0 :%ld", TS_CURRENT, TS_MIN,
  469                (ts_val) timeofday);
  470 
  471     /* sendto one(cptr, "CAPAB ...."); moved to after PASS but before SERVER
  472      * now in two places.. up above and in s_bsd.c. - lucas
  473      * This is to make sure we pass on our capabilities before we establish
  474      * a server connection
  475      */
  476 
  477     /*
  478      * *WARNING*
  479      *   In the following code in place of plain
  480      * server's name we send what is returned by
  481      * get_client_name which may add the "sockhost" after the name.
  482      * It's *very* *important* that there is a SPACE between
  483      * the name and sockhost (if present). The receiving server
  484      * will start the information field from this first blank and
  485      * thus puts the sockhost into info. ...a bit tricky, but
  486      * you have been warned, besides code is more neat this way...
  487      * --msa
  488      */
  489 
  490     cptr->serv->up = me.name;
  491     cptr->serv->aconn = aconn;
  492 
  493     throttle_remove(cipntoa(cptr));
  494 
  495 #ifdef HAVE_ENCRYPTION_ON
  496     if(!CanDoDKEY(cptr) || !WantDKEY(cptr))
  497         return do_server_estab(cptr);
  498     else
  499     {
  500         SetNegoServer(cptr); /* VERY IMPORTANT THAT THIS IS HERE */
  501         sendto_one(cptr, "DKEY START");
  502     }
  503 #else
  504     return do_server_estab(cptr);
  505 #endif
  506 
  507     return 0;
  508 }
  509 
  510 /*
  511  *  m_server
  512  *       parv[0] = sender prefix
  513  *       parv[1] = servername
  514  *       parv[2] = serverinfo/hopcount
  515  *       parv[3] = serverinfo
  516  */
  517 int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[])
  518 {
  519     int     i;
  520     char        info[REALLEN + 1], *host;
  521     aClient    *acptr, *bcptr;
  522     aConnect   *aconn;
  523     int         hop;
  524     char        nbuf[HOSTLEN * 2 + USERLEN + 5]; /* same size as in s_misc.c */
  525 
  526     info[0] = '\0';
  527 
  528     if (parc < 2 || *parv[1] == '\0')
  529     {
  530         sendto_one(cptr, "ERROR :No servername");
  531         return 0;
  532     }
  533 
  534     hop = 0;
  535     host = parv[1];
  536     if (parc > 3 && atoi(parv[2]))
  537     {
  538         hop = atoi(parv[2]);
  539         strncpyzt(info, parv[3], REALLEN + 1);
  540     }
  541     else if (parc > 2)
  542     {
  543         strncpyzt(info, parv[2], REALLEN + 1);
  544         if ((parc > 3) && ((i = strlen(info)) < (REALLEN - 2)))
  545         {
  546             strcat(info, " ");
  547             strncat(info, parv[3], REALLEN - i - 2);
  548             info[REALLEN] = '\0';
  549         }
  550     }
  551     /*
  552      * July 5, 1997
  553      * Rewritten to throw away server cruft from users,
  554      * combined the hostname validity test with cleanup of host name,
  555      * so a cleaned up hostname can be returned as an error if
  556      * necessary. - Dianora
  557      */
  558 
  559     /* yes, the if(strlen) below is really needed!! */
  560     if (strlen(host) > HOSTLEN)
  561         host[HOSTLEN] = '\0';
  562 
  563     if (IsPerson(cptr))
  564     {
  565         /* A local link that has been identified as a USER tries
  566          * something fishy... ;-)
  567          */
  568         sendto_one(cptr, err_str(ERR_UNKNOWNCOMMAND),
  569                    me.name, parv[0], "SERVER");
  570 
  571         return 0;
  572     }
  573     else
  574         /* hostile servername check */
  575     {
  576         /*
  577          * Lets check for bogus names and clean them up we don't bother
  578          * cleaning up ones from users, becasuse we will never see them
  579          * any more - Dianora
  580          */
  581 
  582         int bogus_server = 0;
  583         int found_dot = 0;
  584         char clean_host[(2 * HOSTLEN) + 1];
  585         char *s;
  586         char *d;
  587         int n;
  588 
  589         s = host;
  590         d = clean_host;
  591         n = (2 * HOSTLEN) - 2;
  592 
  593         while (*s && n > 0)
  594         {
  595             if ((unsigned char) *s < (unsigned char) ' ')
  596                 /* Is it a control character? */
  597             {
  598                 bogus_server = 1;
  599                 *d++ = '^';
  600                 *d++ = (char) ((unsigned char) *s + 0x40);
  601                 /* turn it into a printable */
  602                 n -= 2;
  603             }
  604             else if ((unsigned char) *s > (unsigned char) '~')
  605             {
  606                 bogus_server = 1;
  607                 *d++ = '.';
  608                 n--;
  609             }
  610             else
  611             {
  612                 if (*s == '.')
  613                     found_dot = 1;
  614                 *d++ = *s;
  615                 n--;
  616             }
  617             s++;
  618         }
  619         *d = '\0';
  620 
  621         if ((!found_dot) || bogus_server)
  622         {
  623             sendto_one(sptr, "ERROR :Bogus server name (%s)",
  624                        clean_host);
  625             return exit_client(cptr, cptr, cptr, "Bogus server name");
  626         }
  627     }
  628 
  629     /* new connection */
  630     if (IsUnknown(cptr) || IsHandshake(cptr))
  631     {
  632         strncpyzt(cptr->name, host, sizeof(cptr->name));
  633         strncpyzt(cptr->info, info[0] ? info : me.name, REALLEN + 1);
  634         cptr->hopcount = hop;
  635 
  636         switch (check_server_init(cptr))
  637         {
  638             case 0:
  639                 return m_server_estab(cptr);
  640             case 1:
  641                 sendto_ops("Access check for %s in progress",
  642                            get_client_name(cptr, HIDEME));
  643                 return 1;
  644             default:
  645                 ircstp->is_ref++;
  646                 sendto_ops_lev(ADMIN_LEV, "Link %s dropped, no Connect block",
  647                            get_client_name(cptr, TRUE));
  648                 return exit_client(cptr, cptr, cptr, "No Connect block");
  649         }
  650     }
  651     
  652     /* already linked server */
  653     if (!IsServer(cptr))
  654         return 0;
  655 
  656     if ((acptr = find_name(host, NULL)))
  657     {
  658         /*
  659          * * This link is trying feed me a server that I already have
  660          * access through another path -- multiple paths not accepted
  661          * currently, kill this link immediately!!
  662          *
  663          * Rather than KILL the link which introduced it, KILL the
  664          * youngest of the two links. -avalon
  665          */
  666 
  667         bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr :
  668             acptr->from;
  669         sendto_one(bcptr, "ERROR :Server %s already exists", host);
  670         if (bcptr == cptr)
  671         {
  672             /* Don't complain for servers that are juped */
  673             /* (don't complain if the server that already exists is U: lined,
  674                 unless I actually have a .conf U: line for it */
  675             if(!IsULine(acptr) || find_aUserver(acptr->name))
  676             {
  677                 sendto_gnotice("from %s: Link %s cancelled, server %s already "
  678                                "exists", me.name, get_client_name(bcptr, HIDEME),
  679                                host);
  680                 sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, "
  681                                    "server %s already exists", me.name,
  682                                    get_client_name(bcptr, HIDEME), host);
  683             }
  684             return exit_client(bcptr, bcptr, &me, "Server Exists");
  685         }
  686         /* inform all those who care (set +n) -epi */
  687         strcpy(nbuf, get_client_name(bcptr, HIDEME));
  688         sendto_gnotice("from %s: Link %s cancelled, server %s reintroduced "
  689                        "by %s", me.name, nbuf, host,
  690                        get_client_name(cptr, HIDEME));
  691         sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s "
  692                            "reintroduced by %s", me.name, nbuf, host,
  693                            get_client_name(cptr, HIDEME));
  694         exit_client(bcptr, bcptr, &me, "Server Exists");
  695     }
  696     /*
  697      * The following if statement would be nice to remove since user
  698      * nicks never have '.' in them and servers must always have '.' in
  699      * them. There should never be a server/nick name collision, but it
  700      * is possible a capricious server admin could deliberately do
  701      * something strange.
  702      *
  703      * -Dianora
  704      */
  705 
  706     if ((acptr = find_client(host, NULL)) && acptr != cptr)
  707     {
  708         /*
  709          * * Server trying to use the same name as a person. Would
  710          * cause a fair bit of confusion. Enough to make it hellish for
  711          * a while and servers to send stuff to the wrong place.
  712          */
  713         sendto_one(cptr, "ERROR :Nickname %s already exists!", host);
  714         strcpy(nbuf, get_client_name(cptr, HIDEME));
  715         sendto_gnotice("from %s: Link %s cancelled, servername/nick collision",
  716                        me.name, nbuf);
  717         sendto_serv_butone(cptr, ":%s GNOTICE :Link %s cancelled, "
  718                            "servername/nick collision", me.name, nbuf);
  719         return exit_client(cptr, cptr, cptr, "Nick as Server");
  720     }
  721 
  722     if (IsServer(cptr))
  723     {
  724         /*
  725          * * Server is informing about a new server behind this link.
  726          * Create REMOTE server structure, add it to list and propagate
  727          * word to my other server links...
  728          */
  729         if (parc == 1 || info[0] == '\0')
  730         {
  731             sendto_one(cptr, "ERROR :No server info specified for %s", host);
  732             return 0;
  733         }
  734         /*
  735          * * See if the newly found server is behind a guaranteed leaf
  736          * (L-line). If so, close the link.
  737          *
  738          * Depreciated.  Kinda redundant with Hlines. -epi
  739          */
  740         if (!(cptr->serv->aconn->flags & CONN_HUB))
  741         {
  742             aconn = cptr->serv->aconn;
  743             sendto_gnotice("from %s: Non-Hub link %s introduced %s",
  744                            me.name, get_client_name(cptr, HIDEME), host);
  745             sendto_serv_butone(cptr,":%s GNOTICE :Non-Hub link %s introduced "
  746                                "%s", me.name, get_client_name(cptr, HIDEME),
  747                                host);
  748             sendto_one(cptr, "ERROR :You're not a hub (introducing %s)",
  749                        host);
  750             return exit_client(cptr, cptr, cptr, "Too many servers");
  751         }
  752 
  753         acptr = make_client(cptr, sptr);
  754         make_server(acptr);
  755         acptr->hopcount = hop;
  756         strncpyzt(acptr->name, host, sizeof(acptr->name));
  757         strncpyzt(acptr->info, info, REALLEN + 1);
  758         acptr->serv->up = find_or_add(parv[0]);
  759 
  760         fakelinkserver_update(acptr->name, acptr->info);
  761         SetServer(acptr);
  762 
  763         /*
  764          * if this server is behind a U-lined server, make it U-lined as
  765          * well. - lucas
  766          */
  767 
  768         if (IsULine(sptr) || find_aUserver(acptr->name))
  769         {
  770             acptr->flags |= FLAGS_ULINE;
  771             sendto_realops_lev(DEBUG_LEV, "%s introducing super server %s",
  772                                cptr->name, acptr->name);
  773         }
  774 
  775         Count.server++;
  776 
  777         add_client_to_list(acptr);
  778         add_to_client_hash_table(acptr->name, acptr);
  779         /*
  780          * Old sendto_serv_but_one() call removed because we now need
  781          * to send different names to different servers (domain name matching)
  782          */
  783         for (i = 0; i <= highest_fd; i++)
  784         {
  785             if (!(bcptr = local[i]) || !IsServer(bcptr) || bcptr == cptr ||
  786                 IsMe(bcptr))
  787                 continue;
  788             if (!(aconn = bcptr->serv->aconn))
  789             {
  790                 sendto_gnotice("from %s: Lost Connect block for %s on %s."
  791                                " Closing", me.name,
  792                                get_client_name(cptr, HIDEME), host);
  793                 sendto_serv_butone(cptr, ":%s GNOTICE :Lost Connect block for"
  794                                    " %s on %s. Closing", me.name,
  795                                    get_client_name(cptr, HIDEME), host);
  796                 return exit_client(cptr, cptr, cptr, "Lost Connect block");
  797             }
  798             if (match(my_name_for_link(me.name, aconn), acptr->name) == 0)
  799                 continue;
  800             sendto_one(bcptr, ":%s SERVER %s %d :%s",
  801                        parv[0], acptr->name, hop + 1, acptr->info);
  802         }
  803         return 0;
  804     }
  805 
  806     return 0;
  807 }
  808 
  809 /* m_dkey
  810  * lucas's code, i assume.
  811  * moved here from s_serv.c due to its integration in the encrypted 
  812  * server negotiation stuffs. -epi
  813  */
  814 
  815 #define DKEY_GOTIN  0x01
  816 #define DKEY_GOTOUT 0x02
  817 
  818 #define DKEY_DONE(x) (((x) & (DKEY_GOTIN|DKEY_GOTOUT)) == \
  819                       (DKEY_GOTIN|DKEY_GOTOUT))
  820 
  821 int m_dkey(aClient *cptr, aClient *sptr, int parc, char *parv[])
  822 {
  823     if(!(IsNegoServer(sptr) && parc > 1))
  824     {
  825         if(IsPerson(sptr))
  826             return 0;
  827         return exit_client(sptr, sptr, sptr, "Not negotiating now");
  828     }
  829 #ifdef HAVE_ENCRYPTION_ON
  830     if(mycmp(parv[1], "START") == 0)
  831     {
  832         char keybuf[1024];
  833 
  834         if(parc != 2)
  835             return exit_client(sptr, sptr, sptr, "DKEY START failure");
  836 
  837         if(sptr->serv->sessioninfo_in != NULL &&
  838            sptr->serv->sessioninfo_out != NULL)
  839             return exit_client(sptr, sptr, sptr, "DKEY START duplicate?!");
  840 
  841         sptr->serv->sessioninfo_in = dh_start_session();
  842         sptr->serv->sessioninfo_out = dh_start_session();
  843 
  844         sendto_realops("Initiating diffie-hellman key exchange with %s",
  845                        sptr->name);
  846 
  847         dh_get_s_public(keybuf, 1024, sptr->serv->sessioninfo_in);
  848         sendto_one(sptr, "DKEY PUB I %s", keybuf);
  849 
  850         dh_get_s_public(keybuf, 1024, sptr->serv->sessioninfo_out);
  851         sendto_one(sptr, "DKEY PUB O %s", keybuf);
  852         return 0;
  853     }
  854 
  855     if(mycmp(parv[1], "PUB") == 0)
  856     {
  857         unsigned char keybuf[1024];
  858         size_t keylen;
  859 
  860         if(parc != 4 || !sptr->serv->sessioninfo_in ||
  861            !sptr->serv->sessioninfo_out)
  862             return exit_client(sptr, sptr, sptr, "DKEY PUB failure");
  863 
  864         if(mycmp(parv[2], "O") == 0) /* their out is my in! */
  865         {
  866             if(!dh_generate_shared(sptr->serv->sessioninfo_in, parv[3]))
  867                 return exit_client(sptr, sptr, sptr, "DKEY PUB O invalid");
  868             sptr->serv->dkey_flags |= DKEY_GOTOUT;
  869         }
  870         else if(mycmp(parv[2], "I") == 0) /* their out is my in! */
  871         {
  872             if(!dh_generate_shared(sptr->serv->sessioninfo_out, parv[3]))
  873                 return exit_client(sptr, sptr, sptr, "DKEY PUB I invalid");
  874             sptr->serv->dkey_flags |= DKEY_GOTIN;
  875         }
  876         else
  877             return exit_client(sptr, sptr, sptr, "DKEY PUB bad option");
  878 
  879         if(DKEY_DONE(sptr->serv->dkey_flags))
  880         {
  881             sendto_one(sptr, "DKEY DONE");
  882             SetRC4OUT(sptr);
  883 
  884             keylen = 1024;
  885             if(!dh_get_s_shared(keybuf, &keylen, sptr->serv->sessioninfo_in))
  886                 return exit_client(sptr, sptr, sptr,
  887                                    "Could not setup encrypted session");
  888             sptr->serv->rc4_in = rc4_initstate(keybuf, keylen);
  889 
  890             keylen = 1024;
  891             if(!dh_get_s_shared(keybuf, &keylen, sptr->serv->sessioninfo_out))
  892                 return exit_client(sptr, sptr, sptr,
  893                                    "Could not setup encrypted session");
  894             sptr->serv->rc4_out = rc4_initstate(keybuf, keylen);
  895 
  896             dh_end_session(sptr->serv->sessioninfo_in);
  897             dh_end_session(sptr->serv->sessioninfo_out);
  898 
  899             sptr->serv->sessioninfo_in = sptr->serv->sessioninfo_out = NULL;
  900             return 0;
  901         }
  902 
  903         return 0;
  904     }
  905 
  906 
  907     if(mycmp(parv[1], "DONE") == 0)
  908     {
  909         if(!((sptr->serv->sessioninfo_in == NULL &&
  910               sptr->serv->sessioninfo_out == NULL) &&
  911              (sptr->serv->rc4_in != NULL && sptr->serv->rc4_out != NULL)))
  912             return exit_client(sptr, sptr, sptr, "DKEY DONE when not done!");
  913         SetRC4IN(sptr);
  914         sendto_realops("Diffie-Hellman exchange with %s complete, connection "
  915                        "encrypted.", sptr->name);
  916         sendto_one(sptr, "DKEY EXIT");
  917         return RC4_NEXT_BUFFER;
  918     }
  919 
  920     if(mycmp(parv[1], "EXIT") == 0)
  921     {
  922         if(!(IsRC4IN(sptr) && IsRC4OUT(sptr)))
  923             return exit_client(sptr, sptr, sptr, "DKEY EXIT when not in "
  924                                "proper stage");
  925         ClearNegoServer(sptr);
  926         return do_server_estab(sptr);
  927     }
  928 #endif
  929     return 0;
  930 }
  931