X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Firc-server.c;h=8526a573ec361b0851222314343e079b447114f6;hp=d342ffab754f463ce423f842c41f56e9f37c192c;hb=343a90dc376eb9979151752ec33c64ca45b04802;hpb=47ca178a219d682c589b27e64ee1a4e936cc7bdc diff --git a/src/ngircd/irc-server.c b/src/ngircd/irc-server.c index d342ffab..8526a573 100644 --- a/src/ngircd/irc-server.c +++ b/src/ngircd/irc-server.c @@ -7,14 +7,14 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. - * - * IRC commands for server links */ - #include "portab.h" -static char UNUSED id[] = "$Id: irc-server.c,v 1.46 2007/11/21 12:16:36 alex Exp $"; +/** + * @file + * IRC commands for server links + */ #include "imp.h" #include @@ -24,11 +24,10 @@ static char UNUSED id[] = "$Id: irc-server.c,v 1.46 2007/11/21 12:16:36 alex Exp #include #include "defines.h" -#include "resolve.h" #include "conn.h" +#include "conn-func.h" #include "conn-zip.h" #include "conf.h" -#include "client.h" #include "channel.h" #include "irc-write.h" #include "lists.h" @@ -38,6 +37,7 @@ static char UNUSED id[] = "$Id: irc-server.c,v 1.46 2007/11/21 12:16:36 alex Exp #include "numeric.h" #include "ngircd.h" #include "irc-info.h" +#include "op.h" #include "exp.h" #include "irc-server.h" @@ -50,9 +50,8 @@ static char UNUSED id[] = "$Id: irc-server.c,v 1.46 2007/11/21 12:16:36 alex Exp GLOBAL bool IRC_SERVER( CLIENT *Client, REQUEST *Req ) { - char str[LINE_LEN], *ptr; + char str[LINE_LEN]; CLIENT *from, *c; - bool ok; int i; CONN_ID con; @@ -64,79 +63,82 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req ) return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG, Client_ID(Client), Req->command); - if (Client_Type(Client) == CLIENT_GOTPASS) { + if (Client_Type(Client) == CLIENT_GOTPASS || + Client_Type(Client) == CLIENT_GOTPASS_2813) { /* We got a PASS command from the peer, and now a SERVER * command: the peer tries to register itself as a server. */ LogDebug("Connection %d: got SERVER command (new server link) ...", Client_Conn(Client)); - /* Falsche Anzahl Parameter? */ if(( Req->argc != 2 ) && ( Req->argc != 3 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); - /* Ist dieser Server bei uns konfiguriert? */ + /* Ist this server configured on out side? */ for( i = 0; i < MAX_SERVERS; i++ ) if( strcasecmp( Req->argv[0], Conf_Server[i].name ) == 0 ) break; if( i >= MAX_SERVERS ) { - /* Server ist nicht konfiguriert! */ Log( LOG_ERR, "Connection %d: Server \"%s\" not configured here!", Client_Conn( Client ), Req->argv[0] ); Conn_Close( Client_Conn( Client ), NULL, "Server not configured here", true); return DISCONNECTED; } - if( strcmp( Client_Password( Client ), Conf_Server[i].pwd_in ) != 0 ) + if( strcmp( Conn_Password( Client_Conn( Client ) ), + Conf_Server[i].pwd_in ) != 0 ) { - /* Falsches Passwort */ + /* wrong password */ Log( LOG_ERR, "Connection %d: Got bad password from server \"%s\"!", Client_Conn( Client ), Req->argv[0] ); Conn_Close( Client_Conn( Client ), NULL, "Bad password", true); return DISCONNECTED; } - /* Ist ein Server mit dieser ID bereits registriert? */ + /* Is there a registered server with this ID? */ if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED; - /* Server-Strukturen fuellen ;-) */ Client_SetID( Client, Req->argv[0] ); Client_SetHops( Client, 1 ); Client_SetInfo( Client, Req->argv[Req->argc - 1] ); - /* Meldet sich der Server bei uns an (d.h., bauen nicht wir - * selber die Verbindung zu einem anderen Server auf)? */ - con = Client_Conn( Client ); - if( Client_Token( Client ) != TOKEN_OUTBOUND ) - { - /* Eingehende Verbindung: Unseren SERVER- und PASS-Befehl senden */ - ok = true; - if( ! IRC_WriteStrClient( Client, "PASS %s %s", Conf_Server[i].pwd_out, NGIRCd_ProtoID )) ok = false; - else ok = IRC_WriteStrClient( Client, "SERVER %s 1 :%s", Conf_ServerName, Conf_ServerInfo ); - if( ! ok ) - { - Conn_Close( con, "Unexpected server behavior!", NULL, false ); - return DISCONNECTED; + /* Is this server registering on our side, or are we connecting to + * a remote server? */ + con = Client_Conn(Client); + if (Client_Token(Client) != TOKEN_OUTBOUND) { + /* Incoming connection, send user/pass */ + if (!IRC_WriteStrClient(Client, "PASS %s %s", + Conf_Server[i].pwd_out, + NGIRCd_ProtoID) + || !IRC_WriteStrClient(Client, "SERVER %s 1 :%s", + Conf_ServerName, + Conf_ServerInfo)) { + Conn_Close(con, "Unexpected server behavior!", + NULL, false); + return DISCONNECTED; } - Client_SetIntroducer( Client, Client ); - Client_SetToken( Client, 1 ); - } - else - { - /* Ausgehende verbindung, SERVER und PASS wurden von uns bereits - * an die Gegenseite uerbermittelt */ - Client_SetToken( Client, atoi( Req->argv[1] )); + Client_SetIntroducer(Client, Client); + Client_SetToken(Client, 1); + } else { + /* outgoing connect, we already sent a SERVER and PASS + * command to the peer */ + Client_SetToken(Client, atoi(Req->argv[1])); } /* Mark this connection as belonging to an configured server */ Conf_SetServer(i, con); - + + /* Check protocol level */ + if (Client_Type(Client) == CLIENT_GOTPASS) { + /* We got a "simple" PASS command, so the peer is + * using the protocol as defined in RFC 1459. */ + if (! (Conn_Options(con) & CONN_RFC1459)) + Log(LOG_INFO, + "Switching connection %d (\"%s\") to RFC 1459 compatibility mode.", + con, Client_ID(Client)); + Conn_SetOption(con, CONN_RFC1459); + } + Client_SetType(Client, CLIENT_UNKNOWNSERVER); #ifdef ZLIB - /* Kompression initialisieren, wenn erforderlich */ - if( strchr( Client_Flags( Client ), 'Z' )) - { - if( ! Zip_InitConn( con )) - { - /* Fehler! */ - Conn_Close( con, "Can't inizialize compression (zlib)!", NULL, false ); - return DISCONNECTED; - } + if (strchr(Client_Flags(Client), 'Z') && !Zip_InitConn(con)) { + Conn_Close( con, "Can't inizialize compression (zlib)!", NULL, false ); + return DISCONNECTED; } #endif @@ -160,43 +162,34 @@ IRC_SERVER( CLIENT *Client, REQUEST *Req ) } else if( Client_Type( Client ) == CLIENT_SERVER ) { - /* Neuer Server wird im Netz angekuendigt */ + /* New server is being introduced to the network */ - /* Falsche Anzahl Parameter? */ if( Req->argc != 4 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); - /* Ist ein Server mit dieser ID bereits registriert? */ + /* check for existing server with same ID */ if( ! Client_CheckID( Client, Req->argv[0] )) return DISCONNECTED; - /* Ueberfluessige Hostnamen aus Info-Text entfernen */ - ptr = strchr( Req->argv[3] + 2, '[' ); - if( ! ptr ) ptr = Req->argv[3]; - from = Client_Search( Req->prefix ); if( ! from ) { - /* Hm, Server, der diesen einfuehrt, ist nicht bekannt!? */ + /* Uh, Server, that introduced the new server is unknown?! */ Log( LOG_ALERT, "Unknown ID in prefix of SERVER: \"%s\"! (on connection %d)", Req->prefix, Client_Conn( Client )); Conn_Close( Client_Conn( Client ), NULL, "Unknown ID in prefix of SERVER", true); return DISCONNECTED; } - /* Neue Client-Struktur anlegen */ - c = Client_NewRemoteServer( Client, Req->argv[0], from, atoi( Req->argv[1] ), atoi( Req->argv[2] ), ptr, true); - if( ! c ) - { - /* Neue Client-Struktur konnte nicht angelegt werden */ + c = Client_NewRemoteServer(Client, Req->argv[0], from, atoi(Req->argv[1]), atoi(Req->argv[2]), Req->argv[3], true); + if (!c) { Log( LOG_ALERT, "Can't create client structure for server! (on connection %d)", Client_Conn( Client )); Conn_Close( Client_Conn( Client ), NULL, "Can't allocate client structure for remote server", true); return DISCONNECTED; } - /* Log-Meldung zusammenbauen und ausgeben */ if(( Client_Hops( c ) > 1 ) && ( Req->prefix[0] )) snprintf( str, sizeof( str ), "connected to %s, ", Client_ID( from )); else strcpy( str, "" ); Log( LOG_NOTICE|LOG_snotice, "Server \"%s\" registered (via %s, %s%d hop%s).", Client_ID( c ), Client_ID( Client ), str, Client_Hops( c ), Client_Hops( c ) > 1 ? "s": "" ); - /* Andere Server informieren */ + /* notify other servers */ IRC_WriteStrServersPrefix( Client, from, "SERVER %s %d %d :%s", Client_ID( c ), Client_Hops( c ) + 1, Client_MyToken( c ), Client_Info( c )); return CONNECTED; @@ -217,7 +210,6 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req ) assert( Client != NULL ); assert( Req != NULL ); - /* Falsche Anzahl Parameter? */ if( Req->argc != 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); strlcpy( nick_in, Req->argv[1], sizeof( nick_in )); @@ -229,7 +221,7 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req ) { is_op = is_voiced = false; - /* Prefixe abschneiden */ + /* cut off prefixes */ while(( *ptr == '@' ) || ( *ptr == '+' )) { if( *ptr == '@' ) is_op = true; @@ -247,14 +239,14 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req ) if( is_op ) Channel_UserModeAdd( chan, c, 'o' ); if( is_voiced ) Channel_UserModeAdd( chan, c, 'v' ); - /* im Channel bekannt machen */ + /* announce to channel... */ IRC_WriteStrChannelPrefix( Client, chan, c, false, "JOIN :%s", channame ); - /* Channel-User-Modes setzen */ + /* set Channel-User-Modes */ strlcpy( modes, Channel_UserModes( chan, c ), sizeof( modes )); if( modes[0] ) { - /* Modes im Channel bekannt machen */ + /* send modes to channel */ IRC_WriteStrChannelPrefix( Client, chan, Client, false, "MODE %s +%s %s", channame, modes, Client_ID( c )); } @@ -265,60 +257,115 @@ IRC_NJOIN( CLIENT *Client, REQUEST *Req ) } else Log( LOG_ERR, "Got NJOIN for unknown nick \"%s\" for channel \"%s\"!", ptr, channame ); - /* naechsten Nick suchen */ + /* search for next Nick */ ptr = strtok( NULL, "," ); } - /* an andere Server weiterleiten */ + /* forward to other servers */ if( nick_out[0] != '\0' ) IRC_WriteStrServersPrefix( Client, Client_ThisServer( ), "NJOIN %s :%s", Req->argv[0], nick_out ); return CONNECTED; } /* IRC_NJOIN */ +/** + * Handler for the IRC command "SQUIT". + * See RFC 2813 section 4.1.2 and RFC 2812 section 3.1.8. + */ GLOBAL bool -IRC_SQUIT( CLIENT *Client, REQUEST *Req ) +IRC_SQUIT(CLIENT * Client, REQUEST * Req) { - CLIENT *target; - char msg[LINE_LEN + 64]; + char msg[COMMAND_LEN], logmsg[COMMAND_LEN]; + CLIENT *from, *target; + CONN_ID con; + int loglevel; - assert( Client != NULL ); - assert( Req != NULL ); + assert(Client != NULL); + assert(Req != NULL); - /* Falsche Anzahl Parameter? */ - if( Req->argc != 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + if (Client_Type(Client) != CLIENT_SERVER + && !Client_HasMode(Client, 'o')) + return Op_NoPrivileges(Client, Req); - Log( LOG_DEBUG, "Got SQUIT from %s for \"%s\": \"%s\" ...", Client_ID( Client ), Req->argv[0], Req->argv[1] ); + /* Bad number of arguments? */ + if (Req->argc != 2) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); - target = Client_Search( Req->argv[0] ); - if( ! target ) - { - /* Den Server kennen wir nicht (mehr), also nichts zu tun. */ - Log( LOG_WARNING, "Got SQUIT from %s for unknown server \"%s\"!?", Client_ID( Client ), Req->argv[0] ); + if (Client_Type(Client) == CLIENT_SERVER && Req->prefix) { + from = Client_Search(Req->prefix); + if (Client_Type(from) != CLIENT_SERVER + && !Op_Check(Client, Req)) + return Op_NoPrivileges(Client, Req); + } else + from = Client; + if (!from) + return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG, + Client_ID(Client), Req->prefix); + + if (Client_Type(Client) == CLIENT_USER) + loglevel = LOG_NOTICE | LOG_snotice; + else + loglevel = LOG_DEBUG; + Log(loglevel, "Got SQUIT from %s for \"%s\": \"%s\" ...", + Client_ID(from), Req->argv[0], Req->argv[1]); + + target = Client_Search(Req->argv[0]); + if (Client_Type(Client) != CLIENT_SERVER && + target == Client_ThisServer()) + return Op_NoPrivileges(Client, Req); + if (!target) { + /* The server is (already) unknown */ + Log(LOG_WARNING, + "Got SQUIT from %s for unknown server \"%s\"!?", + Client_ID(Client), Req->argv[0]); return CONNECTED; } - if( Req->argv[1][0] ) - { - if( strlen( Req->argv[1] ) > LINE_LEN ) Req->argv[1][LINE_LEN] = '\0'; - snprintf( msg, sizeof( msg ), "%s (SQUIT from %s).", Req->argv[1], Client_ID( Client )); - } - else snprintf( msg, sizeof( msg ), "Got SQUIT from %s.", Client_ID( Client )); + con = Client_Conn(target); - if( Client_Conn( target ) > NONE ) - { - /* dieser Server hat die Connection */ - if( Req->argv[1][0] ) Conn_Close( Client_Conn( target ), msg, Req->argv[1], true); - else Conn_Close( Client_Conn( target ), msg, NULL, true); - return DISCONNECTED; - } + if (Req->argv[1][0]) + if (Client_NextHop(from) != Client || con > NONE) + snprintf(msg, sizeof(msg), "%s (SQUIT from %s)", + Req->argv[1], Client_ID(from)); + else + strlcpy(msg, Req->argv[1], sizeof(msg)); else - { - /* Verbindung hielt anderer Server */ - Client_Destroy( target, msg, Req->argv[1], false ); - return CONNECTED; + snprintf(msg, sizeof(msg), "Got SQUIT from %s", + Client_ID(from)); + + if (con > NONE) { + /* We are directly connected to the target server, so we + * have to tear down the connection and to inform all the + * other remaining servers in the network */ + IRC_SendWallops(Client_ThisServer(), Client_ThisServer(), + "Received SQUIT %s from %s: %s", + Req->argv[0], Client_ID(from), + Req->argv[1][0] ? Req->argv[1] : "-"); + Conn_Close(con, NULL, msg, true); + if (con == Client_Conn(Client)) + return DISCONNECTED; + } else { + /* This server is not directly connected, so the SQUIT must + * be forwarded ... */ + if (Client_Type(from) != CLIENT_SERVER) { + /* The origin is not an IRC server, so don't evaluate + * this SQUIT but simply forward it */ + IRC_WriteStrClientPrefix(Client_NextHop(target), + from, "SQUIT %s :%s", Req->argv[0], Req->argv[1]); + } else { + /* SQUIT has been generated by another server, so + * remove the target server from the network! */ + logmsg[0] = '\0'; + if (!strchr(msg, '(')) + snprintf(logmsg, sizeof(logmsg), + "%s (SQUIT from %s)", Req->argv[1], + Client_ID(from)); + Client_Destroy(target, logmsg[0] ? logmsg : msg, + msg, false); + } } + return CONNECTED; } /* IRC_SQUIT */ - /* -eof- */