/* Bahamut IRCd, src/m_server.c * Copyright (c) 2004, Aaron Wiebe and the Bahamut Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id: m_server.c,v 1.7 2006/01/07 22:13:26 trystanscott Exp $ */ #include "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "h.h" #include "dh.h" #include "userban.h" #include "zlink.h" #include "throttle.h" #include "clones.h" /* externally defined functions */ extern void fakelinkserver_update(char *, char *); extern void fakeserver_sendserver(aClient *); extern void fakelusers_sendlock(aClient *); extern void reset_sock_opts(int, int); extern void sync_lists(aClient *cptr); /* internal functions */ static void sendnick_TS(aClient *cptr, aClient *acptr) { static char ubuf[12]; if (IsPerson(acptr)) { send_umode(NULL, acptr, 0, SEND_UMODES, ubuf); if (!*ubuf) /* trivial optimization - Dianora */ { ubuf[0] = '+'; ubuf[1] = '\0'; } sendto_one(cptr, "NICK %s %d %ld %s %s %s %s %lu %lu :%s", acptr->name, acptr->hopcount + 1, acptr->tsinfo, ubuf, acptr->user->username, MyConnect(acptr) ? acptr->sockhost : acptr->user->realhost, acptr->user->server, acptr->user->servicestamp, htonl(acptr->ip.s_addr), acptr->info); if (IsUmodev(acptr)) sendto_one(cptr, "SVHOST %s %s", acptr->name, acptr->user->host); } } static int do_server_estab(aClient *cptr) { aClient *acptr; aConnect *aconn; aChannel *chptr; int i; /* "refresh" inpath with host */ char *inpath = get_client_name(cptr, HIDEME); SetServer(cptr); Count.unknown--; Count.server++; Count.myserver++; if(IsZipCapable(cptr) && DoZipThis(cptr)) { sendto_one(cptr, "SVINFO ZIP"); SetZipOut(cptr); cptr->serv->zip_out = zip_create_output_session(); } #ifdef MAXBUFFERS /* let's try to bump up server sock_opts... -Taner */ reset_sock_opts(cptr->fd, 1); #endif /* adds to server list */ add_to_list(&server_list, cptr); set_effective_class(cptr); /* Check one more time for good measure... is it there? */ if ((acptr = find_name(cptr->name, NULL))) { char nbuf[HOSTLEN * 2 + USERLEN + 5]; aClient *bcptr; /* * While negotiating stuff, another copy of this server appeared. * * Rather than KILL the link which introduced it, KILL the * youngest of the two links. -avalon */ bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr : acptr->from; sendto_one(bcptr, "ERROR :Server %s already exists", cptr->name); if (bcptr == cptr) { sendto_gnotice("from %s: Link %s cancelled, server %s already " "exists (final phase)", me.name, get_client_name(bcptr, HIDEME), cptr->name); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, " "server %s already exists (final phase)", me.name, get_client_name(bcptr, HIDEME), cptr->name); return exit_client(bcptr, bcptr, &me, "Server Exists (final phase)"); } /* inform all those who care (set +n) -epi */ strcpy(nbuf, get_client_name(bcptr, HIDEME)); sendto_gnotice("from %s: Link %s cancelled, server %s reintroduced " "by %s (final phase)", me.name, nbuf, cptr->name, get_client_name(cptr, HIDEME)); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s " "reintroduced by %s (final phase)", me.name, nbuf, cptr->name, get_client_name(cptr, HIDEME)); exit_client(bcptr, bcptr, &me, "Server Exists (final phase)"); } /* error, error, error! if a server is U:'d, and it connects to us, * we need to figure that out! So, do it here. - lucas */ if (find_aUserver(cptr->name)) { Count.myulined++; cptr->flags |= FLAGS_ULINE; /* special flags (should really be in conf) */ if (!mycmp(cptr->name, Services_Name)) cptr->serv->uflags |= (ULF_SFDIRECT|ULF_REQTARGET|ULF_NOBTOPIC|ULF_NOAWAY); else if (!mycmp(cptr->name, Stats_Name)) cptr->serv->uflags |= ULF_NOBTOPIC; } fakelinkserver_update(cptr->name, cptr->info); sendto_gnotice("from %s: Link with %s established, states:%s%s%s%s", me.name, inpath, ZipOut(cptr) ? " Output-compressed" : "", RC4EncLink(cptr) ? " encrypted" : "", IsULine(cptr) ? " ULined" : "", DoesTS(cptr) ? " TS" : " Non-TS"); /* * Notify everyone of the fact that this has just linked: the entire * network should get two of these, one explaining the link between * me->serv and the other between serv->me */ sendto_serv_butone(NULL, ":%s GNOTICE :Link with %s established: %s", me.name, inpath, DoesTS(cptr) ? "TS link" : "Non-TS link!"); add_to_client_hash_table(cptr->name, cptr); /* add it to scache */ find_or_add(cptr->name); /* * Old sendto_serv_but_one() call removed because we now need to * send different names to different servers (domain name * matching) Send new server to other servers. */ for (i = 0; i <= highest_fd; i++) { if (!(acptr = local[i]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) continue; if ((aconn = acptr->serv->aconn) && !match(my_name_for_link(me.name, aconn), cptr->name)) continue; sendto_one(acptr, ":%s SERVER %s 2 :%s", me.name, cptr->name, cptr->info); } /* * Pass on my client information to the new server * * First, pass only servers (idea is that if the link gets * cancelled beacause the server was already there, there are no * NICK's to be cancelled...). Of course, if cancellation occurs, * all this info is sent anyway, and I guess the link dies when a * read is attempted...? --msa * * Note: Link cancellation to occur at this point means that at * least two servers from my fragment are building up connection * this other fragment at the same time, it's a race condition, * not the normal way of operation... * * ALSO NOTE: using the get_client_name for server names-- see * previous *WARNING*!!! (Also, original inpath is * destroyed...) */ aconn = cptr->serv->aconn; for (acptr = &me; acptr; acptr = acptr->prev) { if (acptr->from == cptr) continue; if (IsServer(acptr)) { if (match(my_name_for_link(me.name, aconn), acptr->name) == 0) continue; sendto_one(cptr, ":%s SERVER %s %d :%s", acptr->serv->up, acptr->name, acptr->hopcount + 1, acptr->info); } } /* send out our SQLINES and SGLINES too */ send_simbans(cptr, SBAN_CHAN|SBAN_NETWORK); send_simbans(cptr, SBAN_NICK|SBAN_NETWORK); send_simbans(cptr, SBAN_GCOS|SBAN_NETWORK); /* Send out fake server list and other 'fake' stuff */ fakeserver_sendserver(cptr); /* send clone list */ clones_send(cptr); /* Bursts are about to start.. send a BURST */ if (IsBurst(cptr)) sendto_one(cptr, "BURST"); /* * * Send it in the shortened format with the TS, if it's a TS * server; walk the list of channels, sending all the nicks that * haven't been sent yet for each channel, then send the channel * itself -- it's less obvious than sending all nicks first, but * on the receiving side memory will be allocated more nicely * saving a few seconds in the handling of a split -orabidoo */ { chanMember *cm; static char nickissent = 1; nickissent = 3 - nickissent; /* * flag used for each nick to check if we've sent it yet - must * be different each time and !=0, so we alternate between 1 and * 2 -orabidoo */ for (chptr = channel; chptr; chptr = chptr->nextch) { for (cm = chptr->members; cm; cm = cm->next) { acptr = cm->cptr; if (acptr->nicksent != nickissent) { acptr->nicksent = nickissent; if (acptr->from != cptr) sendnick_TS(cptr, acptr); } } send_channel_modes(cptr, chptr); } /* also send out those that are not on any channel */ for (acptr = &me; acptr; acptr = acptr->prev) if (acptr->nicksent != nickissent) { acptr->nicksent = nickissent; if (acptr->from != cptr) sendnick_TS(cptr, acptr); } sync_lists(cptr); /* Send out shuns and g-lines */ } if(confopts & FLAGS_HUB) fakelusers_sendlock(cptr); if(ZipOut(cptr)) { unsigned long inb, outb; double rat; zip_out_get_stats(cptr->serv->zip_out, &inb, &outb, &rat); if(inb) { sendto_gnotice("from %s: Connect burst to %s: %lu bytes normal, " "%lu compressed (%3.2f%%)", me.name, get_client_name(cptr, HIDEME), inb, outb, rat); sendto_serv_butone(cptr, ":%s GNOTICE :Connect burst to %s: %lu " "bytes normal, %lu compressed (%3.2f%%)", me.name, get_client_name(cptr, HIDEME), inb, outb, rat); } } /* stuff a PING at the end of this burst so we can figure out when the other side has finished processing it. */ cptr->flags |= FLAGS_BURST|FLAGS_PINGSENT; if (IsBurst(cptr)) cptr->flags |= FLAGS_SOBSENT; sendto_one(cptr, "PING :%s", me.name); return 0; } static int m_server_estab(aClient *cptr) { aConnect *aconn; char *inpath, *host, *s, *encr; int split; inpath = get_client_name(cptr, HIDEME); /* "refresh" inpath with host */ split = mycmp(cptr->name, cptr->sockhost); host = cptr->name; if (!(aconn = cptr->serv->aconn)) { ircstp->is_ref++; sendto_one(cptr, "ERROR :Lost Connect block"); sendto_ops_lev(ADMIN_LEV, "Lost Connect block for server %s", get_client_name(cptr, TRUE)); return exit_client(cptr, cptr, cptr, "Lost Connect block"); } encr = cptr->passwd; if (*aconn->apasswd && !StrEq(aconn->apasswd, encr)) { ircstp->is_ref++; sendto_one(cptr, "ERROR :Wrong link password", inpath); sendto_ops("Link %s dropped, wrong password", inpath); return exit_client(cptr, cptr, cptr, "Bad Password"); } memset(cptr->passwd, '\0', sizeof(cptr->passwd)); if (find_client(host, NULL)) { sendto_gnotice("from %s: Link %s dropped, server already exists", me.name, inpath); sendto_serv_butone(cptr, ":%s GNOTICE :Link %s dropped, server already" " exists", me.name, inpath); return exit_client(cptr, cptr, cptr, "Server Exists"); } if(!(confopts & FLAGS_HUB)) { int i; for (i = 0; i <= highest_fd; i++) if (local[i] && IsServer(local[i])) { ircstp->is_ref++; sendto_one(cptr, "ERROR :I'm a leaf not a hub"); return exit_client(cptr, cptr, cptr, "I'm a leaf"); } } /* aconf->port is a CAPAB field, kind-of. kludge. mm, mm. */ /* no longer! this should still get better though */ if((aconn->flags & CONN_ZIP)) SetZipCapable(cptr); if((aconn->flags & CONN_DKEY)) SetWantDKEY(cptr); if (IsUnknown(cptr)) { if (aconn->cpasswd[0]) sendto_one(cptr, "PASS %s :TS", aconn->cpasswd); /* Pass my info to the new server */ #ifdef HAVE_ENCRYPTION_ON if(!WantDKEY(cptr)) sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP " "NICKIP TSMODE"); else sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT DKEY " "ZIP NICKIP TSMODE"); #else sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP NICKIP TSMODE"); #endif sendto_one(cptr, "SERVER %s 1 :%s", my_name_for_link(me.name, aconn), (me.info[0]) ? (me.info) : "IRCers United"); } else { s = (char *) strchr(aconn->host, '@'); *s = '\0'; /* should never be NULL -- wanna bet? -Dianora */ Debug((DEBUG_INFO, "Check Usernames [%s]vs[%s]", aconn->host, cptr->username)); if (match(aconn->host, cptr->username)) { *s = '@'; ircstp->is_ref++; sendto_ops("Username mismatch [%s]v[%s] : %s", aconn->host, cptr->username, get_client_name(cptr, HIDEME)); sendto_one(cptr, "ERROR :No Username Match"); return exit_client(cptr, cptr, cptr, "Bad User"); } *s = '@'; } /* send routing notice, this should never happen anymore */ if (!DoesTS(cptr)) { sendto_gnotice("from %s: Warning: %s linked, non-TS server", me.name, get_client_name(cptr, HIDEME)); sendto_serv_butone(cptr, ":%s GNOTICE :Warning: %s linked, non-TS server", me.name, get_client_name(cptr, HIDEME)); } sendto_one(cptr, "SVINFO %d %d 0 :%ld", TS_CURRENT, TS_MIN, (ts_val) timeofday); /* sendto one(cptr, "CAPAB ...."); moved to after PASS but before SERVER * now in two places.. up above and in s_bsd.c. - lucas * This is to make sure we pass on our capabilities before we establish * a server connection */ /* * *WARNING* * In the following code in place of plain * server's name we send what is returned by * get_client_name which may add the "sockhost" after the name. * It's *very* *important* that there is a SPACE between * the name and sockhost (if present). The receiving server * will start the information field from this first blank and * thus puts the sockhost into info. ...a bit tricky, but * you have been warned, besides code is more neat this way... * --msa */ cptr->serv->up = me.name; cptr->serv->aconn = aconn; throttle_remove(inetntoa((char *)&cptr->ip)); #ifdef HAVE_ENCRYPTION_ON if(!CanDoDKEY(cptr) || !WantDKEY(cptr)) return do_server_estab(cptr); else { SetNegoServer(cptr); /* VERY IMPORTANT THAT THIS IS HERE */ sendto_one(cptr, "DKEY START"); } #else return do_server_estab(cptr); #endif return 0; } /* * m_server * parv[0] = sender prefix * parv[1] = servername * parv[2] = serverinfo/hopcount * parv[3] = serverinfo */ int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[]) { int i; char info[REALLEN + 1], *inpath, *host; aClient *acptr, *bcptr; aConnect *aconn; int hop; char nbuf[HOSTLEN * 2 + USERLEN + 5]; /* same size as in s_misc.c */ info[0] = '\0'; inpath = get_client_name(cptr, HIDEME); if (parc < 2 || *parv[1] == '\0') { sendto_one(cptr, "ERROR :No servername"); return 0; } hop = 0; host = parv[1]; if (parc > 3 && atoi(parv[2])) { hop = atoi(parv[2]); strncpyzt(info, parv[3], REALLEN); } else if (parc > 2) { strncpyzt(info, parv[2], REALLEN); if ((parc > 3) && ((i = strlen(info)) < (REALLEN - 2))) { strcat(info, " "); strncat(info, parv[3], REALLEN - i - 2); info[REALLEN] = '\0'; } } /* * July 5, 1997 * Rewritten to throw away server cruft from users, * combined the hostname validity test with cleanup of host name, * so a cleaned up hostname can be returned as an error if * necessary. - Dianora */ /* yes, the if(strlen) below is really needed!! */ if (strlen(host) > HOSTLEN) host[HOSTLEN] = '\0'; if (IsPerson(cptr)) { /* A local link that has been identified as a USER tries * something fishy... ;-) */ sendto_one(cptr, err_str(ERR_UNKNOWNCOMMAND), me.name, parv[0], "SERVER"); return 0; } else /* hostile servername check */ { /* * Lets check for bogus names and clean them up we don't bother * cleaning up ones from users, becasuse we will never see them * any more - Dianora */ int bogus_server = 0; int found_dot = 0; char clean_host[(2 * HOSTLEN) + 1]; char *s; char *d; int n; s = host; d = clean_host; n = (2 * HOSTLEN) - 2; while (*s && n > 0) { if ((unsigned char) *s < (unsigned char) ' ') /* Is it a control character? */ { bogus_server = 1; *d++ = '^'; *d++ = (char) ((unsigned char) *s + 0x40); /* turn it into a printable */ n -= 2; } else if ((unsigned char) *s > (unsigned char) '~') { bogus_server = 1; *d++ = '.'; n--; } else { if (*s == '.') found_dot = 1; *d++ = *s; n--; } s++; } *d = '\0'; if ((!found_dot) || bogus_server) { sendto_one(sptr, "ERROR :Bogus server name (%s)", clean_host); return exit_client(cptr, cptr, cptr, "Bogus server name"); } } /* new connection */ if (IsUnknown(cptr) || IsHandshake(cptr)) { strncpyzt(cptr->name, host, sizeof(cptr->name)); strncpyzt(cptr->info, info[0] ? info : me.name, REALLEN); cptr->hopcount = hop; switch (check_server_init(cptr)) { case 0: return m_server_estab(cptr); case 1: sendto_ops("Access check for %s in progress", get_client_name(cptr, HIDEME)); return 1; default: ircstp->is_ref++; sendto_ops_lev(ADMIN_LEV, "Link %s dropped, no Connect block", get_client_name(cptr, TRUE)); return exit_client(cptr, cptr, cptr, "No Connect block"); } } /* already linked server */ if (!IsServer(cptr)) return 0; if ((acptr = find_name(host, NULL))) { /* * * This link is trying feed me a server that I already have * access through another path -- multiple paths not accepted * currently, kill this link immediately!! * * Rather than KILL the link which introduced it, KILL the * youngest of the two links. -avalon */ bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr : acptr->from; sendto_one(bcptr, "ERROR :Server %s already exists", host); if (bcptr == cptr) { /* Don't complain for servers that are juped */ /* (don't complain if the server that already exists is U: lined, unless I actually have a .conf U: line for it */ if(!IsULine(acptr) || find_aUserver(acptr->name)) { sendto_gnotice("from %s: Link %s cancelled, server %s already " "exists", me.name, get_client_name(bcptr, HIDEME), host); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, " "server %s already exists", me.name, get_client_name(bcptr, HIDEME), host); } return exit_client(bcptr, bcptr, &me, "Server Exists"); } /* inform all those who care (set +n) -epi */ strcpy(nbuf, get_client_name(bcptr, HIDEME)); sendto_gnotice("from %s: Link %s cancelled, server %s reintroduced " "by %s", me.name, nbuf, host, get_client_name(cptr, HIDEME)); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s " "reintroduced by %s", me.name, nbuf, host, get_client_name(cptr, HIDEME)); exit_client(bcptr, bcptr, &me, "Server Exists"); } /* * The following if statement would be nice to remove since user * nicks never have '.' in them and servers must always have '.' in * them. There should never be a server/nick name collision, but it * is possible a capricious server admin could deliberately do * something strange. * * -Dianora */ if ((acptr = find_client(host, NULL)) && acptr != cptr) { /* * * Server trying to use the same name as a person. Would * cause a fair bit of confusion. Enough to make it hellish for * a while and servers to send stuff to the wrong place. */ sendto_one(cptr, "ERROR :Nickname %s already exists!", host); strcpy(nbuf, get_client_name(cptr, HIDEME)); sendto_gnotice("from %s: Link %s cancelled, servername/nick collision", me.name, nbuf); sendto_serv_butone(cptr, ":%s GNOTICE :Link %s cancelled, " "servername/nick collision", me.name, nbuf); return exit_client(cptr, cptr, cptr, "Nick as Server"); } 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... */ if (parc == 1 || info[0] == '\0') { sendto_one(cptr, "ERROR :No server info specified for %s", host); return 0; } /* * * See if the newly found server is behind a guaranteed leaf * (L-line). If so, close the link. * * Depreciated. Kinda redundant with Hlines. -epi */ if (!(cptr->serv->aconn->flags & CONN_HUB)) { aconn = cptr->serv->aconn; sendto_gnotice("from %s: Non-Hub link %s introduced %s", me.name, get_client_name(cptr, HIDEME), host); sendto_serv_butone(cptr,":%s GNOTICE :Non-Hub link %s introduced " "%s", me.name, get_client_name(cptr, HIDEME), host); sendto_one(cptr, "ERROR :You're not a hub (introducing %s)", host); return exit_client(cptr, cptr, cptr, "Too many servers"); } acptr = make_client(cptr, sptr); make_server(acptr); acptr->hopcount = hop; strncpyzt(acptr->name, host, sizeof(acptr->name)); strncpyzt(acptr->info, info, REALLEN); acptr->serv->up = find_or_add(parv[0]); fakelinkserver_update(acptr->name, acptr->info); SetServer(acptr); /* * if this server is behind a U-lined server, make it U-lined as * well. - lucas */ if (IsULine(sptr) || find_aUserver(acptr->name)) { acptr->flags |= FLAGS_ULINE; sendto_realops_lev(DEBUG_LEV, "%s introducing super server %s", cptr->name, acptr->name); } Count.server++; add_client_to_list(acptr); add_to_client_hash_table(acptr->name, 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 <= highest_fd; i++) { if (!(bcptr = local[i]) || !IsServer(bcptr) || bcptr == cptr || IsMe(bcptr)) continue; if (!(aconn = bcptr->serv->aconn)) { sendto_gnotice("from %s: Lost Connect block for %s on %s." " Closing", me.name, get_client_name(cptr, HIDEME), host); sendto_serv_butone(cptr, ":%s GNOTICE :Lost Connect block for" " %s on %s. Closing", me.name, get_client_name(cptr, HIDEME), host); return exit_client(cptr, cptr, cptr, "Lost Connect block"); } if (match(my_name_for_link(me.name, aconn), acptr->name) == 0) continue; sendto_one(bcptr, ":%s SERVER %s %d :%s", parv[0], acptr->name, hop + 1, acptr->info); } return 0; } return 0; } /* m_dkey * lucas's code, i assume. * moved here from s_serv.c due to its integration in the encrypted * server negotiation stuffs. -epi */ #define DKEY_GOTIN 0x01 #define DKEY_GOTOUT 0x02 #define DKEY_DONE(x) (((x) & (DKEY_GOTIN|DKEY_GOTOUT)) == \ (DKEY_GOTIN|DKEY_GOTOUT)) int m_dkey(aClient *cptr, aClient *sptr, int parc, char *parv[]) { if(!(IsNegoServer(sptr) && parc > 1)) { if(IsPerson(sptr)) return 0; return exit_client(sptr, sptr, sptr, "Not negotiating now"); } #ifdef HAVE_ENCRYPTION_ON if(mycmp(parv[1], "START") == 0) { char keybuf[1024]; if(parc != 2) return exit_client(sptr, sptr, sptr, "DKEY START failure"); if(sptr->serv->sessioninfo_in != NULL && sptr->serv->sessioninfo_out != NULL) return exit_client(sptr, sptr, sptr, "DKEY START duplicate?!"); sptr->serv->sessioninfo_in = dh_start_session(); sptr->serv->sessioninfo_out = dh_start_session(); sendto_realops("Initiating diffie-hellman key exchange with %s", sptr->name); dh_get_s_public(keybuf, 1024, sptr->serv->sessioninfo_in); sendto_one(sptr, "DKEY PUB I %s", keybuf); dh_get_s_public(keybuf, 1024, sptr->serv->sessioninfo_out); sendto_one(sptr, "DKEY PUB O %s", keybuf); return 0; } if(mycmp(parv[1], "PUB") == 0) { char keybuf[1024]; int keylen; if(parc != 4 || !sptr->serv->sessioninfo_in || !sptr->serv->sessioninfo_out) return exit_client(sptr, sptr, sptr, "DKEY PUB failure"); if(mycmp(parv[2], "O") == 0) /* their out is my in! */ { if(!dh_generate_shared(sptr->serv->sessioninfo_in, parv[3])) return exit_client(sptr, sptr, sptr, "DKEY PUB O invalid"); sptr->serv->dkey_flags |= DKEY_GOTOUT; } else if(mycmp(parv[2], "I") == 0) /* their out is my in! */ { if(!dh_generate_shared(sptr->serv->sessioninfo_out, parv[3])) return exit_client(sptr, sptr, sptr, "DKEY PUB I invalid"); sptr->serv->dkey_flags |= DKEY_GOTIN; } else return exit_client(sptr, sptr, sptr, "DKEY PUB bad option"); if(DKEY_DONE(sptr->serv->dkey_flags)) { sendto_one(sptr, "DKEY DONE"); SetRC4OUT(sptr); keylen = 1024; if(!dh_get_s_shared(keybuf, &keylen, sptr->serv->sessioninfo_in)) return exit_client(sptr, sptr, sptr, "Could not setup encrypted session"); sptr->serv->rc4_in = rc4_initstate((unsigned char *) keybuf, keylen); keylen = 1024; if(!dh_get_s_shared(keybuf, &keylen, sptr->serv->sessioninfo_out)) return exit_client(sptr, sptr, sptr, "Could not setup encrypted session"); sptr->serv->rc4_out = rc4_initstate((unsigned char *) keybuf, keylen); dh_end_session(sptr->serv->sessioninfo_in); dh_end_session(sptr->serv->sessioninfo_out); sptr->serv->sessioninfo_in = sptr->serv->sessioninfo_out = NULL; return 0; } return 0; } if(mycmp(parv[1], "DONE") == 0) { if(!((sptr->serv->sessioninfo_in == NULL && sptr->serv->sessioninfo_out == NULL) && (sptr->serv->rc4_in != NULL && sptr->serv->rc4_out != NULL))) return exit_client(sptr, sptr, sptr, "DKEY DONE when not done!"); SetRC4IN(sptr); sendto_realops("Diffie-Hellman exchange with %s complete, connection " "encrypted.", sptr->name); sendto_one(sptr, "DKEY EXIT"); return RC4_NEXT_BUFFER; } if(mycmp(parv[1], "EXIT") == 0) { if(!(IsRC4IN(sptr) && IsRC4OUT(sptr))) return exit_client(sptr, sptr, sptr, "DKEY EXIT when not in " "proper stage"); ClearNegoServer(sptr); return do_server_estab(sptr); } #endif return 0; }