2 * ngIRCd -- The Next Generation IRC Daemon
3 * Copyright (c)2001-2024 Alexander Barton (alex@barton.de) and Contributors.
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.
16 * IRC commands for server links
25 #include "conn-func.h"
35 #include "irc-channel.h"
37 #include "irc-write.h"
40 #include "irc-server.h"
43 * Handler for the IRC "SERVER" command.
45 * @param Client The client from which this command has been received.
46 * @param Req Request structure with prefix and all parameters.
47 * @return CONNECTED or DISCONNECTED.
50 IRC_SERVER( CLIENT *Client, REQUEST *Req )
56 assert( Client != NULL );
57 assert( Req != NULL );
59 /* Return an error if this is not a local client */
60 if (Client_Conn(Client) <= NONE)
61 return IRC_WriteErrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
62 Client_ID(Client), Req->command);
64 if (Client_Type(Client) == CLIENT_GOTPASS ||
65 Client_Type(Client) == CLIENT_GOTPASS_2813) {
66 /* We got a PASS command from the peer, and now a SERVER
67 * command: the peer tries to register itself as a server. */
68 LogDebug("Connection %d: got SERVER command (new server link) ...",
71 if (Req->argc != 2 && Req->argc != 3)
72 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
76 /* Get configuration index of new remote server ... */
77 for (i = 0; i < MAX_SERVERS; i++)
78 if (strcasecmp(Req->argv[0], Conf_Server[i].name) == 0)
81 /* Make sure the remote server is configured here */
82 if (i >= MAX_SERVERS) {
84 "Connection %d: Server \"%s\" not configured here!",
85 Client_Conn(Client), Req->argv[0]);
86 Conn_Close(Client_Conn(Client), NULL,
87 "Server not configured here", true);
92 /* Does this server require an SSL connection? */
93 if (Conf_Server[i].SSLConnect &&
94 !(Conn_Options(Client_Conn(Client)) & CONN_SSL)) {
96 "Connection %d: Server \"%s\" requires a secure connection!",
97 Client_Conn(Client), Req->argv[0]);
98 Conn_Close(Client_Conn(Client), NULL,
99 "Secure connection required", true);
104 /* Check server password */
105 if (strcmp(Conn_Password(Client_Conn(Client)),
106 Conf_Server[i].pwd_in) != 0) {
108 "Connection %d: Got bad password from server \"%s\"!",
109 Client_Conn(Client), Req->argv[0]);
110 Conn_Close(Client_Conn(Client), NULL,
111 "Bad password", true);
115 /* Is there a registered server with this ID? */
116 if (!Client_CheckID(Client, Req->argv[0]))
119 /* Mark this connection as belonging to an configured server */
120 if (!Conf_SetServer(i, Client_Conn(Client)))
123 Client_SetID( Client, Req->argv[0] );
124 Client_SetHops( Client, 1 );
125 Client_SetInfo( Client, Req->argv[Req->argc - 1] );
127 /* Is this server registering on our side, or are we connecting to
128 * a remote server? */
129 if (Client_Token(Client) != TOKEN_OUTBOUND) {
130 /* Incoming connection, send user/pass */
131 if (!IRC_WriteStrClient(Client, "PASS %s %s",
132 Conf_Server[i].pwd_out,
134 || !IRC_WriteStrClient(Client, "SERVER %s 1 :%s",
137 Conn_Close(Client_Conn(Client),
138 "Unexpected server behavior!",
142 Client_SetIntroducer(Client, Client);
143 Client_SetToken(Client, 1);
145 /* outgoing connect, we already sent a SERVER and PASS
146 * command to the peer */
147 Client_SetToken(Client, atoi(Req->argv[1]));
150 /* Check protocol level */
151 if (Client_Type(Client) == CLIENT_GOTPASS) {
152 /* We got a "simple" PASS command, so the peer is
153 * using the protocol as defined in RFC 1459. */
154 if (! (Conn_Options(Client_Conn(Client)) & CONN_RFC1459))
156 "Switching connection %d (\"%s\") to RFC 1459 compatibility mode.",
157 Client_Conn(Client), Client_ID(Client));
158 Conn_SetOption(Client_Conn(Client), CONN_RFC1459);
161 Client_SetType(Client, CLIENT_UNKNOWNSERVER);
164 if (Client_HasFlag(Client, 'Z')
165 && !Zip_InitConn(Client_Conn(Client))) {
166 Conn_Close(Client_Conn(Client),
167 "Can't initialize compression (zlib)!",
174 if (Client_HasFlag(Client, 'H')) {
175 LogDebug("Peer supports IRC+ extended server handshake ...");
176 if (!IRC_Send_ISUPPORT(Client))
178 return IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG,
182 if (Conf_MaxNickLength != CLIENT_NICK_LEN_DEFAULT)
184 "Attention: this server uses a non-standard nick length, but the peer doesn't support the IRC+ extended server handshake!");
189 return IRC_Num_ENDOFMOTD(Client, Req);
191 else if( Client_Type( Client ) == CLIENT_SERVER )
193 /* New server is being introduced to the network */
196 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
197 Client_ID(Client), Req->command);
199 /* check for existing server with same ID */
200 if (!Client_CheckID(Client, Req->argv[0]))
204 /* We definitely need a prefix here! */
205 Log(LOG_ALERT, "Got SERVER command without prefix! (on connection %d)",
206 Client_Conn(Client));
207 Conn_Close(Client_Conn(Client), NULL,
208 "SERVER command without prefix", true);
212 from = Client_Search( Req->prefix );
214 /* Uh, Server, that introduced the new server is unknown?! */
216 "Unknown ID in prefix of SERVER: \"%s\"! (on connection %d)",
217 Req->prefix, Client_Conn(Client));
218 Conn_Close(Client_Conn(Client), NULL,
219 "Unknown ID in prefix of SERVER", true);
223 c = Client_NewRemoteServer(Client, Req->argv[0], from,
224 atoi(Req->argv[1]), atoi(Req->argv[2]),
228 "Can't create client structure for server! (on connection %d)",
229 Client_Conn(Client));
230 Conn_Close(Client_Conn(Client), NULL,
231 "Can't allocate client structure for remote server",
236 if (Client_Hops(c) > 1 && Req->prefix[0])
237 snprintf(str, sizeof(str), "connected to %s, ",
241 Log(LOG_NOTICE|LOG_snotice,
242 "Server \"%s\" registered (via %s, %s%d hop%s).",
243 Client_ID(c), Client_ID(Client), str, Client_Hops(c),
244 Client_Hops(c) > 1 ? "s": "" );
246 /* notify other servers */
247 IRC_WriteStrServersPrefix(Client, from, "SERVER %s %d %d :%s",
248 Client_ID(c), Client_Hops(c) + 1,
249 Client_MyToken(c), Client_Info(c));
253 return IRC_WriteErrClient(Client, ERR_NEEDMOREPARAMS_MSG,
254 Client_ID(Client), Req->command);
258 * Handler for the IRC "NJOIN" command.
260 * @param Client The client from which this command has been received.
261 * @param Req Request structure with prefix and all parameters.
262 * @return CONNECTED or DISCONNECTED.
265 IRC_NJOIN( CLIENT *Client, REQUEST *Req )
267 char nick_in[COMMAND_LEN], nick_out[COMMAND_LEN], *channame, *ptr, modes[8];
268 bool is_owner, is_chanadmin, is_op, is_halfop, is_voiced;
272 assert(Client != NULL);
275 strlcpy(nick_in, Req->argv[1], sizeof(nick_in));
276 strcpy(nick_out, "");
278 channame = Req->argv[0];
280 ptr = strtok(nick_in, ",");
282 is_owner = is_chanadmin = is_op = is_halfop = is_voiced = false;
284 /* cut off prefixes */
285 while ((*ptr == '~') || (*ptr == '&') || (*ptr == '@') ||
286 (*ptr == '%') || (*ptr == '+')) {
300 c = Client_Search(ptr);
302 /* Client not found? */
304 "Got NJOIN for unknown nick \"%s\" for channel \"%s\"!",
309 if (!Channel_Join(c, channame)) {
310 /* Failed to join channel. Ooops!? */
312 "Failed to join client \"%s\" to channel \"%s\" (NJOIN): killing it!",
314 IRC_KillClient(NULL, NULL, ptr, "Internal NJOIN error!");
315 LogDebug("... done.");
319 chan = Channel_Search(channame);
320 assert(chan != NULL);
323 Channel_UserModeAdd(chan, c, 'q');
325 Channel_UserModeAdd(chan, c, 'a');
327 Channel_UserModeAdd(chan, c, 'o');
329 Channel_UserModeAdd(chan, c, 'h');
331 Channel_UserModeAdd(chan, c, 'v');
333 /* Announce client to the channel */
334 IRC_WriteStrChannelPrefix(Client, chan, c, false,
335 "JOIN :%s", channame);
337 /* If the client is connected to this server, it was remotely
338 * joined to the channel by another server/service: So send
339 * TOPIC and NAMES messages like on a regular JOIN command! */
340 if(Client_Conn(c) != NONE)
341 IRC_Send_Channel_Info(c, chan);
343 /* Announce "channel user modes" to the channel, if any */
344 strlcpy(modes, Channel_UserModes(chan, c), sizeof(modes));
346 IRC_WriteStrChannelPrefix(Client, chan, Client, false,
347 "MODE %s +%s %s", channame,
348 modes, Client_ID(c));
350 /* Build nick list for forwarding command */
351 if (nick_out[0] != '\0')
352 strlcat(nick_out, ",", sizeof(nick_out));
354 strlcat(nick_out, "~", sizeof(nick_out));
356 strlcat(nick_out, "&", sizeof(nick_out));
358 strlcat(nick_out, "@", sizeof(nick_out));
360 strlcat(nick_out, "%", sizeof(nick_out));
362 strlcat(nick_out, "+", sizeof(nick_out));
363 strlcat(nick_out, ptr, sizeof(nick_out));
366 /* Get next nick, if any ... */
367 ptr = strtok(NULL, ",");
370 /* forward to other servers */
371 if (nick_out[0] != '\0')
372 IRC_WriteStrServersPrefix(Client, Client_ThisServer(),
373 "NJOIN %s :%s", Req->argv[0], nick_out);
379 * Handler for the IRC "SQUIT" command.
381 * @param Client The client from which this command has been received.
382 * @param Req Request structure with prefix and all parameters.
383 * @return CONNECTED or DISCONNECTED.
386 IRC_SQUIT(CLIENT * Client, REQUEST * Req)
388 char msg[COMMAND_LEN], logmsg[COMMAND_LEN];
389 CLIENT *from, *target;
390 CONN_ID con, client_con;
393 assert(Client != NULL);
396 if (Client_Type(Client) != CLIENT_SERVER
397 && !Client_HasMode(Client, 'o'))
398 return Op_NoPrivileges(Client, Req);
400 if (Client_Type(Client) == CLIENT_SERVER && Req->prefix) {
401 from = Client_Search(Req->prefix);
402 if (Client_Type(from) != CLIENT_SERVER
403 && !Op_Check(Client, Req))
404 return Op_NoPrivileges(Client, Req);
408 return IRC_WriteErrClient(Client, ERR_NOSUCHNICK_MSG,
409 Client_ID(Client), Req->prefix);
411 if (Client_Type(Client) == CLIENT_USER)
412 loglevel = LOG_NOTICE | LOG_snotice;
414 loglevel = LOG_DEBUG;
415 Log(loglevel, "Got SQUIT from %s for \"%s\": \"%s\" ...",
416 Client_ID(from), Req->argv[0], Req->argv[1]);
418 target = Client_Search(Req->argv[0]);
419 if (Client_Type(Client) != CLIENT_SERVER &&
420 target == Client_ThisServer())
421 return Op_NoPrivileges(Client, Req);
423 /* The server is (already) unknown */
425 "Got SQUIT from %s for unknown server \"%s\"!?",
426 Client_ID(Client), Req->argv[0]);
430 client_con = Client_Conn(Client);
431 con = Client_Conn(target);
434 if (Client_NextHop(from) != Client || con > NONE)
435 snprintf(msg, sizeof(msg), "\"%s\" (SQUIT from %s)",
436 Req->argv[1], Client_ID(from));
438 strlcpy(msg, Req->argv[1], sizeof(msg));
440 snprintf(msg, sizeof(msg), "Got SQUIT from %s",
444 /* We are directly connected to the target server, so we
445 * have to tear down the connection and to inform all the
446 * other remaining servers in the network */
447 IRC_SendWallops(Client_ThisServer(), Client_ThisServer(),
448 "Received SQUIT %s from %s: %s",
449 Req->argv[0], Client_ID(from),
450 Req->argv[1][0] ? Req->argv[1] : "-");
451 Conn_Close(con, NULL, msg, true);
452 if (con == client_con)
455 /* This server is not directly connected, so the SQUIT must
456 * be forwarded ... */
457 if (Client_Type(from) != CLIENT_SERVER) {
458 /* The origin is not an IRC server, so don't evaluate
459 * this SQUIT but simply forward it */
460 IRC_WriteStrClientPrefix(Client_NextHop(target),
461 from, "SQUIT %s :%s", Req->argv[0], Req->argv[1]);
463 /* SQUIT has been generated by another server, so
464 * remove the target server from the network! */
466 if (!strchr(msg, '('))
467 snprintf(logmsg, sizeof(logmsg),
468 "\"%s\" (SQUIT from %s)", Req->argv[1],
470 Client_Destroy(target, logmsg[0] ? logmsg : msg,