Annotation of ircnowd/src/ngircd/irc-server.c, Revision 1.1.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