Annotation of ircnowd/src/ngircd/irc-server.c, Revision 1.1
1.1 ! tomglok 1: /*
! 2: * ngIRCd -- The Next Generation IRC Daemon
! 3: * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify
! 6: * it under the terms of the GNU General Public License as published by
! 7: * the Free Software Foundation; either version 2 of the License, or
! 8: * (at your option) any later version.
! 9: * Please read the file COPYING, README and AUTHORS for more information.
! 10: */
! 11:
! 12: #include "portab.h"
! 13:
! 14: /**
! 15: * @file
! 16: * IRC commands for server links
! 17: */
! 18:
! 19: #include <assert.h>
! 20: #include <stdio.h>
! 21: #include <stdlib.h>
! 22: #include <string.h>
! 23: #include <strings.h>
! 24:
! 25: #include "conn-func.h"
! 26: #include "conn-zip.h"
! 27: #include "conf.h"
! 28: #include "channel.h"
! 29: #include "log.h"
! 30: #include "messages.h"
! 31: #include "parse.h"
! 32: #include "numeric.h"
! 33: #include "ngircd.h"
! 34: #include "irc.h"
! 35: #include "irc-info.h"
! 36: #include "irc-write.h"
! 37: #include "op.h"
! 38:
! 39: #include "irc-server.h"
! 40:
! 41: /**
! 42: * Handler for the IRC "SERVER" command.
! 43: *
! 44: * @param Client The client from which this command has been received.
! 45: * @param Req Request structure with prefix and all parameters.
! 46: * @return CONNECTED or DISCONNECTED.
! 47: */
! 48: GLOBAL bool
! 49: IRC_SERVER( CLIENT *Client, REQUEST *Req )
! 50: {
! 51: char str[100];
! 52: CLIENT *from, *c;
! 53: int i;
! 54:
! 55: assert( Client != NULL );
! 56: assert( Req != NULL );
! 57:
! 58: /* Return an error if this is not a local client */
! 59: if (Client_Conn(Client) <= NONE)
! 60: return IRC_WriteErrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
! 61: Client_ID(Client), Req->command);
! 62:
! 63: if (Client_Type(Client) == CLIENT_GOTPASS ||
! 64: Client_Type(Client) == CLIENT_GOTPASS_2813) {
! 65: /* We got a PASS command from the peer, and now a SERVER
! 66: * command: the peer tries to register itself as a server. */
! 67: LogDebug("Connection %d: got SERVER command (new server link) ...",
! 68: Client_Conn(Client));
! 69:
! 70: if (Req->argc != 2 && Req->argc != 3)
! 71: return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
! 72: Client_ID(Client),
! 73: Req->command);
! 74:
! 75: /* Get configuration index of new remote server ... */
! 76: for (i = 0; i < MAX_SERVERS; i++)
! 77: if (strcasecmp(Req->argv[0], Conf_Server[i].name) == 0)
! 78: break;
! 79:
! 80: /* Make sure the remote server is configured here */
! 81: if (i >= MAX_SERVERS) {
! 82: Log(LOG_ERR,
! 83: "Connection %d: Server \"%s\" not configured here!",
! 84: Client_Conn(Client), Req->argv[0]);
! 85: Conn_Close(Client_Conn(Client), NULL,
! 86: "Server not configured here", true);
! 87: return DISCONNECTED;
! 88: }
! 89:
! 90: /* Check server password */
! 91: if (strcmp(Conn_Password(Client_Conn(Client)),
! 92: Conf_Server[i].pwd_in) != 0) {
! 93: Log(LOG_ERR,
! 94: "Connection %d: Got bad password from server \"%s\"!",
! 95: Client_Conn(Client), Req->argv[0]);
! 96: Conn_Close(Client_Conn(Client), NULL,
! 97: "Bad password", true);
! 98: return DISCONNECTED;
! 99: }
! 100:
! 101: /* Is there a registered server with this ID? */
! 102: if (!Client_CheckID(Client, Req->argv[0]))
! 103: return DISCONNECTED;
! 104:
! 105: /* Mark this connection as belonging to an configured server */
! 106: if (!Conf_SetServer(i, Client_Conn(Client)))
! 107: return DISCONNECTED;
! 108:
! 109: Client_SetID( Client, Req->argv[0] );
! 110: Client_SetHops( Client, 1 );
! 111: Client_SetInfo( Client, Req->argv[Req->argc - 1] );
! 112:
! 113: /* Is this server registering on our side, or are we connecting to
! 114: * a remote server? */
! 115: if (Client_Token(Client) != TOKEN_OUTBOUND) {
! 116: /* Incoming connection, send user/pass */
! 117: if (!IRC_WriteStrClient(Client, "PASS %s %s",
! 118: Conf_Server[i].pwd_out,
! 119: NGIRCd_ProtoID)
! 120: || !IRC_WriteStrClient(Client, "SERVER %s 1 :%s",
! 121: Conf_ServerName,
! 122: Conf_ServerInfo)) {
! 123: Conn_Close(Client_Conn(Client),
! 124: "Unexpected server behavior!",
! 125: NULL, false);
! 126: return DISCONNECTED;
! 127: }
! 128: Client_SetIntroducer(Client, Client);
! 129: Client_SetToken(Client, 1);
! 130: } else {
! 131: /* outgoing connect, we already sent a SERVER and PASS
! 132: * command to the peer */
! 133: Client_SetToken(Client, atoi(Req->argv[1]));
! 134: }
! 135:
! 136: /* Check protocol level */
! 137: if (Client_Type(Client) == CLIENT_GOTPASS) {
! 138: /* We got a "simple" PASS command, so the peer is
! 139: * using the protocol as defined in RFC 1459. */
! 140: if (! (Conn_Options(Client_Conn(Client)) & CONN_RFC1459))
! 141: Log(LOG_INFO,
! 142: "Switching connection %d (\"%s\") to RFC 1459 compatibility mode.",
! 143: Client_Conn(Client), Client_ID(Client));
! 144: Conn_SetOption(Client_Conn(Client), CONN_RFC1459);
! 145: }
! 146:
! 147: Client_SetType(Client, CLIENT_UNKNOWNSERVER);
! 148:
! 149: #ifdef ZLIB
! 150: if (Client_HasFlag(Client, 'Z')
! 151: && !Zip_InitConn(Client_Conn(Client))) {
! 152: Conn_Close(Client_Conn(Client),
! 153: "Can't initialize compression (zlib)!",
! 154: NULL, false );
! 155: return DISCONNECTED;
! 156: }
! 157: #endif
! 158:
! 159: #ifdef IRCPLUS
! 160: if (Client_HasFlag(Client, 'H')) {
! 161: LogDebug("Peer supports IRC+ extended server handshake ...");
! 162: if (!IRC_Send_ISUPPORT(Client))
! 163: return DISCONNECTED;
! 164: return IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG,
! 165: Client_ID(Client));
! 166: } else {
! 167: #endif
! 168: if (Conf_MaxNickLength != CLIENT_NICK_LEN_DEFAULT)
! 169: Log(LOG_CRIT,
! 170: "Attention: this server uses a non-standard nick length, but the peer doesn't support the IRC+ extended server handshake!");
! 171: #ifdef IRCPLUS
! 172: }
! 173: #endif
! 174:
! 175: return IRC_Num_ENDOFMOTD(Client, Req);
! 176: }
! 177: else if( Client_Type( Client ) == CLIENT_SERVER )
! 178: {
! 179: /* New server is being introduced to the network */
! 180:
! 181: if (Req->argc != 4)
! 182: return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
! 183: Client_ID(Client), Req->command);
! 184:
! 185: /* check for existing server with same ID */
! 186: if (!Client_CheckID(Client, Req->argv[0]))
! 187: return DISCONNECTED;
! 188:
! 189: if (!Req->prefix) {
! 190: /* We definitely need a prefix here! */
! 191: Log(LOG_ALERT, "Got SERVER command without prefix! (on connection %d)",
! 192: Client_Conn(Client));
! 193: Conn_Close(Client_Conn(Client), NULL,
! 194: "SERVER command without prefix", true);
! 195: return DISCONNECTED;
! 196: }
! 197:
! 198: from = Client_Search( Req->prefix );
! 199: if (! from) {
! 200: /* Uh, Server, that introduced the new server is unknown?! */
! 201: Log(LOG_ALERT,
! 202: "Unknown ID in prefix of SERVER: \"%s\"! (on connection %d)",
! 203: Req->prefix, Client_Conn(Client));
! 204: Conn_Close(Client_Conn(Client), NULL,
! 205: "Unknown ID in prefix of SERVER", true);
! 206: return DISCONNECTED;
! 207: }
! 208:
! 209: c = Client_NewRemoteServer(Client, Req->argv[0], from,
! 210: atoi(Req->argv[1]), atoi(Req->argv[2]),
! 211: Req->argv[3], true);
! 212: if (!c) {
! 213: Log(LOG_ALERT,
! 214: "Can't create client structure for server! (on connection %d)",
! 215: Client_Conn(Client));
! 216: Conn_Close(Client_Conn(Client), NULL,
! 217: "Can't allocate client structure for remote server",
! 218: true);
! 219: return DISCONNECTED;
! 220: }
! 221:
! 222: if (Client_Hops(c) > 1 && Req->prefix[0])
! 223: snprintf(str, sizeof(str), "connected to %s, ",
! 224: Client_ID(from));
! 225: else
! 226: strcpy(str, "");
! 227: Log(LOG_NOTICE|LOG_snotice,
! 228: "Server \"%s\" registered (via %s, %s%d hop%s).",
! 229: Client_ID(c), Client_ID(Client), str, Client_Hops(c),
! 230: Client_Hops(c) > 1 ? "s": "" );
! 231:
! 232: /* notify other servers */
! 233: IRC_WriteStrServersPrefix(Client, from, "SERVER %s %d %d :%s",
! 234: Client_ID(c), Client_Hops(c) + 1,
! 235: Client_MyToken(c), Client_Info(c));
! 236:
! 237: return CONNECTED;
! 238: } else
! 239: return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
! 240: Client_ID(Client), Req->command);
! 241: } /* IRC_SERVER */
! 242:
! 243: /*
! 244: * Handler for the IRC "NJOIN" command.
! 245: *
! 246: * @param Client The client from which this command has been received.
! 247: * @param Req Request structure with prefix and all parameters.
! 248: * @return CONNECTED or DISCONNECTED.
! 249: */
! 250: GLOBAL bool
! 251: IRC_NJOIN( CLIENT *Client, REQUEST *Req )
! 252: {
! 253: char nick_in[COMMAND_LEN], nick_out[COMMAND_LEN], *channame, *ptr, modes[8];
! 254: bool is_owner, is_chanadmin, is_op, is_halfop, is_voiced;
! 255: CHANNEL *chan;
! 256: CLIENT *c;
! 257:
! 258: assert(Client != NULL);
! 259: assert(Req != NULL);
! 260:
! 261: strlcpy(nick_in, Req->argv[1], sizeof(nick_in));
! 262: strcpy(nick_out, "");
! 263:
! 264: channame = Req->argv[0];
! 265:
! 266: ptr = strtok(nick_in, ",");
! 267: while (ptr) {
! 268: is_owner = is_chanadmin = is_op = is_halfop = is_voiced = false;
! 269:
! 270: /* cut off prefixes */
! 271: while ((*ptr == '~') || (*ptr == '&') || (*ptr == '@') ||
! 272: (*ptr == '%') || (*ptr == '+')) {
! 273: if (*ptr == '~')
! 274: is_owner = true;
! 275: if (*ptr == '&')
! 276: is_chanadmin = true;
! 277: if (*ptr == '@')
! 278: is_op = true;
! 279: if (*ptr == '%')
! 280: is_halfop = true;
! 281: if (*ptr == '+')
! 282: is_voiced = true;
! 283: ptr++;
! 284: }
! 285:
! 286: c = Client_Search(ptr);
! 287: if (!c) {
! 288: /* Client not found? */
! 289: Log(LOG_ERR,
! 290: "Got NJOIN for unknown nick \"%s\" for channel \"%s\"!",
! 291: ptr, channame);
! 292: goto skip_njoin;
! 293: }
! 294:
! 295: if (!Channel_Join(c, channame)) {
! 296: /* Failed to join channel. Ooops!? */
! 297: Log(LOG_ALERT,
! 298: "Failed to join client \"%s\" to channel \"%s\" (NJOIN): killing it!",
! 299: ptr, channame);
! 300: IRC_KillClient(NULL, NULL, ptr, "Internal NJOIN error!");
! 301: Log(LOG_DEBUG, "... done.");
! 302: goto skip_njoin;
! 303: }
! 304:
! 305: chan = Channel_Search(channame);
! 306: assert(chan != NULL);
! 307:
! 308: if (is_owner)
! 309: Channel_UserModeAdd(chan, c, 'q');
! 310: if (is_chanadmin)
! 311: Channel_UserModeAdd(chan, c, 'a');
! 312: if (is_op)
! 313: Channel_UserModeAdd(chan, c, 'o');
! 314: if (is_halfop)
! 315: Channel_UserModeAdd(chan, c, 'h');
! 316: if (is_voiced)
! 317: Channel_UserModeAdd(chan, c, 'v');
! 318:
! 319: /* Announce client to the channel */
! 320: IRC_WriteStrChannelPrefix(Client, chan, c, false,
! 321: "JOIN :%s", channame);
! 322:
! 323: /* Announce "channel user modes" to the channel, if any */
! 324: strlcpy(modes, Channel_UserModes(chan, c), sizeof(modes));
! 325: if (modes[0])
! 326: IRC_WriteStrChannelPrefix(Client, chan, Client, false,
! 327: "MODE %s +%s %s", channame,
! 328: modes, Client_ID(c));
! 329:
! 330: /* Build nick list for forwarding command */
! 331: if (nick_out[0] != '\0')
! 332: strlcat(nick_out, ",", sizeof(nick_out));
! 333: if (is_owner)
! 334: strlcat(nick_out, "~", sizeof(nick_out));
! 335: if (is_chanadmin)
! 336: strlcat(nick_out, "&", sizeof(nick_out));
! 337: if (is_op)
! 338: strlcat(nick_out, "@", sizeof(nick_out));
! 339: if (is_halfop)
! 340: strlcat(nick_out, "%", sizeof(nick_out));
! 341: if (is_voiced)
! 342: strlcat(nick_out, "+", sizeof(nick_out));
! 343: strlcat(nick_out, ptr, sizeof(nick_out));
! 344:
! 345: skip_njoin:
! 346: /* Get next nick, if any ... */
! 347: ptr = strtok(NULL, ",");
! 348: }
! 349:
! 350: /* forward to other servers */
! 351: if (nick_out[0] != '\0')
! 352: IRC_WriteStrServersPrefix(Client, Client_ThisServer(),
! 353: "NJOIN %s :%s", Req->argv[0], nick_out);
! 354:
! 355: return CONNECTED;
! 356: } /* IRC_NJOIN */
! 357:
! 358: /**
! 359: * Handler for the IRC "SQUIT" command.
! 360: *
! 361: * @param Client The client from which this command has been received.
! 362: * @param Req Request structure with prefix and all parameters.
! 363: * @return CONNECTED or DISCONNECTED.
! 364: */
! 365: GLOBAL bool
! 366: IRC_SQUIT(CLIENT * Client, REQUEST * Req)
! 367: {
! 368: char msg[COMMAND_LEN], logmsg[COMMAND_LEN];
! 369: CLIENT *from, *target;
! 370: CONN_ID con, client_con;
! 371: int loglevel;
! 372:
! 373: assert(Client != NULL);
! 374: assert(Req != NULL);
! 375:
! 376: if (Client_Type(Client) != CLIENT_SERVER
! 377: && !Client_HasMode(Client, 'o'))
! 378: return Op_NoPrivileges(Client, Req);
! 379:
! 380: if (Client_Type(Client) == CLIENT_SERVER && Req->prefix) {
! 381: from = Client_Search(Req->prefix);
! 382: if (Client_Type(from) != CLIENT_SERVER
! 383: && !Op_Check(Client, Req))
! 384: return Op_NoPrivileges(Client, Req);
! 385: } else
! 386: from = Client;
! 387: if (!from)
! 388: return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
! 389: Client_ID(Client), Req->prefix);
! 390:
! 391: if (Client_Type(Client) == CLIENT_USER)
! 392: loglevel = LOG_NOTICE | LOG_snotice;
! 393: else
! 394: loglevel = LOG_DEBUG;
! 395: Log(loglevel, "Got SQUIT from %s for \"%s\": \"%s\" ...",
! 396: Client_ID(from), Req->argv[0], Req->argv[1]);
! 397:
! 398: target = Client_Search(Req->argv[0]);
! 399: if (Client_Type(Client) != CLIENT_SERVER &&
! 400: target == Client_ThisServer())
! 401: return Op_NoPrivileges(Client, Req);
! 402: if (!target) {
! 403: /* The server is (already) unknown */
! 404: Log(LOG_WARNING,
! 405: "Got SQUIT from %s for unknown server \"%s\"!?",
! 406: Client_ID(Client), Req->argv[0]);
! 407: return CONNECTED;
! 408: }
! 409:
! 410: client_con = Client_Conn(Client);
! 411: con = Client_Conn(target);
! 412:
! 413: if (Req->argv[1][0])
! 414: if (Client_NextHop(from) != Client || con > NONE)
! 415: snprintf(msg, sizeof(msg), "\"%s\" (SQUIT from %s)",
! 416: Req->argv[1], Client_ID(from));
! 417: else
! 418: strlcpy(msg, Req->argv[1], sizeof(msg));
! 419: else
! 420: snprintf(msg, sizeof(msg), "Got SQUIT from %s",
! 421: Client_ID(from));
! 422:
! 423: if (con > NONE) {
! 424: /* We are directly connected to the target server, so we
! 425: * have to tear down the connection and to inform all the
! 426: * other remaining servers in the network */
! 427: IRC_SendWallops(Client_ThisServer(), Client_ThisServer(),
! 428: "Received SQUIT %s from %s: %s",
! 429: Req->argv[0], Client_ID(from),
! 430: Req->argv[1][0] ? Req->argv[1] : "-");
! 431: Conn_Close(con, NULL, msg, true);
! 432: if (con == client_con)
! 433: return DISCONNECTED;
! 434: } else {
! 435: /* This server is not directly connected, so the SQUIT must
! 436: * be forwarded ... */
! 437: if (Client_Type(from) != CLIENT_SERVER) {
! 438: /* The origin is not an IRC server, so don't evaluate
! 439: * this SQUIT but simply forward it */
! 440: IRC_WriteStrClientPrefix(Client_NextHop(target),
! 441: from, "SQUIT %s :%s", Req->argv[0], Req->argv[1]);
! 442: } else {
! 443: /* SQUIT has been generated by another server, so
! 444: * remove the target server from the network! */
! 445: logmsg[0] = '\0';
! 446: if (!strchr(msg, '('))
! 447: snprintf(logmsg, sizeof(logmsg),
! 448: "\"%s\" (SQUIT from %s)", Req->argv[1],
! 449: Client_ID(from));
! 450: Client_Destroy(target, logmsg[0] ? logmsg : msg,
! 451: msg, false);
! 452: }
! 453: }
! 454: return CONNECTED;
! 455: } /* IRC_SQUIT */
! 456:
! 457: /* -eof- */
CVSweb