2 * IRC - Internet Relay Chat, ircd/m_trace.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * See file AUTHORS in IRC package for additional names of
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 * m_functions execute protocol messages on this server:
29 * cptr is always NON-NULL, pointing to a *LOCAL* client
30 * structure (with an open socket connected!). This
31 * identifies the physical socket where the message
32 * originated (or which caused the m_function to be
33 * executed--some m_functions may call others...).
35 * sptr is the source of the message, defined by the
36 * prefix part of the message if present. If not
37 * or prefix not found, then sptr==cptr.
39 * (!IsServer(cptr)) => (cptr == sptr), because
40 * prefixes are taken *only* from servers...
43 * (sptr == cptr) => the message didn't
46 * (sptr != cptr && IsServer(sptr) means
47 * the prefix specified servername. (?)
49 * (sptr != cptr && !IsServer(sptr) means
50 * that message originated from a remote
55 * (!IsServer(sptr)) means that, sptr can safely
56 * taken as defining the target structure of the
57 * message in this server.
59 * *Always* true (if 'parse' and others are working correct):
61 * 1) sptr->from == cptr (note: cptr->from == cptr)
63 * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64 * *cannot* be a local connection, unless it's
65 * actually cptr!). [MyConnect(x) should probably
66 * be defined as (x == x->from) --msa ]
68 * parc number of variable parameter strings (if zero,
69 * parv is allowed to be NULL)
71 * parv a NULL terminated list of parameter pointers,
73 * parv[0], sender (prefix string), if not present
74 * this points to an empty string.
75 * parv[1]...parv[parc-1]
76 * pointers to additional parameters
77 * parv[parc] == NULL, *always*
79 * note: it is guaranteed that parv[0]..parv[parc-1] are all
84 * No need to include handlers.h here the signatures must match
85 * and we don't need to force a rebuild of all the handlers everytime
86 * we add a new one to the list. --Bleep
94 #include "ircd_reply.h"
95 #include "ircd_string.h"
110 * m_trace - generic message handler
112 * parv[0] = sender prefix
113 * parv[1] = nick or servername
114 * parv[2] = 'target' servername
116 int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
119 struct Client *acptr;
120 const struct ConnectionClass* cl;
123 int link_s[MAXCONNECTIONS];
124 int link_u[MAXCONNECTIONS];
129 if (parc < 2 || BadPtr(parv[1])) {
130 /* just "TRACE" without parameters. Must be from local client */
135 } else if (parc < 3 || BadPtr(parv[2])) {
136 /* No target specified. Make one before propagating. */
139 if ((acptr = find_match_server(parv[1])) ||
140 ((acptr = FindClient(parv[1])) && !MyUser(acptr))) {
142 parv[2] = acptr->user->server->name;
144 parv[2] = acptr->name;
147 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
148 "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
153 /* Got "TRACE <tname> :<target>" */
155 if (MyUser(sptr) || Protocol(cptr) < 10)
156 acptr = find_match_server(parv[2]);
158 acptr = FindNServer(parv[2]);
159 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
160 parv)) == HUNTED_NOSUCH)
165 if (i == HUNTED_PASS) {
167 acptr = next_client(GlobalClientList, tname);
170 send_reply(sptr, RPL_TRACELINK,
172 version, debugmode, tname, acptr ? acptr->from->name : "<No_match>"
174 version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
175 (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0
177 ); /* I really could do without GODMODE */
181 doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : 1;
182 wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
183 dow = wilds || doall;
185 /* Don't give (long) remote listings to lusers */
186 if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
189 for (i = 0; i < MAXCONNECTIONS; i++)
190 link_s[i] = 0, link_u[i] = 0;
193 for (acptr = GlobalClientList; acptr; acptr = acptr->next) {
195 link_u[acptr->from->fd]++;
196 else if (IsServer(acptr))
197 link_s[acptr->from->fd]++;
201 /* report all direct connections */
203 for (i = 0; i <= HighestFd; i++) {
204 unsigned int conClass;
206 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
208 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
209 !IsAnOper(acptr) && (acptr != sptr))
211 if (!doall && wilds && match(tname, acptr->name))
213 if (!dow && 0 != ircd_strcmp(tname, acptr->name))
216 conClass = get_client_class(acptr);
218 switch (acptr->status) {
219 case STAT_CONNECTING:
220 send_reply(sptr, RPL_TRACECONNECTING, conClass, acptr->name);
224 send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, acptr->name);
230 case STAT_UNKNOWN_USER:
231 send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
232 get_client_name(acptr, HIDE_IP));
235 case STAT_UNKNOWN_SERVER:
236 send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
240 /* Only opers see users if there is a wildcard
241 but anyone can see all the opers. */
242 if ((IsAnOper(sptr) && (MyUser(sptr) ||
243 !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
245 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
246 get_client_name(acptr, HIDE_IP),
247 CurrentTime - acptr->lasttime);
249 send_reply(sptr, RPL_TRACEUSER, conClass,
250 get_client_name(acptr, HIDE_IP),
251 CurrentTime - acptr->lasttime);
256 * Connection is a server
258 * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
260 * class Class the server is in
261 * nS Number of servers reached via this link
262 * nC Number of clients reached via this link
263 * name Name of the server linked
264 * ConnBy Who established this link
265 * last Seconds since we got something from this link
266 * age Seconds this link has been alive
268 * Additional comments etc...... -Cym-<cym@acrux.net>
272 if (acptr->serv->user)
273 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
274 link_u[i], acptr->name,
275 (*acptr->serv->by) ? acptr->serv->by : "*",
276 acptr->serv->user->username, acptr->serv->user->host,
277 CurrentTime - acptr->lasttime,
278 CurrentTime - acptr->serv->timestamp);
280 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
281 link_u[i], acptr->name,
282 (*acptr->serv->by) ? acptr->serv->by : "*", "*",
283 me.name, CurrentTime - acptr->lasttime,
284 CurrentTime - acptr->serv->timestamp);
287 default: /* We actually shouldn't come here, -msa */
288 send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
294 * Add these lines to summarize the above which can get rather long
295 * and messy when done remotely - Avalon
297 if (!IsAnOper(sptr) || !cnt) {
299 /* let the user have some idea that its at the end of the trace */
300 send_reply(sptr, RPL_TRACESERVER, 0, link_s[me.fd],
301 link_u[me.fd], "<No_match>", *(me.serv->by) ?
302 me.serv->by : "*", "*", me.name, 0, 0);
306 for (cl = get_class_list(); cl; cl = cl->next) {
308 send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
315 * ms_trace - server message handler
317 * parv[0] = sender prefix
318 * parv[1] = nick or servername
319 * parv[2] = 'target' servername
321 int ms_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
324 struct Client *acptr;
325 const struct ConnectionClass* cl;
328 int link_s[MAXCONNECTIONS];
329 int link_u[MAXCONNECTIONS];
334 if (parc < 2 || BadPtr(parv[1])) {
335 /* just "TRACE" without parameters. Must be from local client */
340 } else if (parc < 3 || BadPtr(parv[2])) {
341 /* No target specified. Make one before propagating. */
344 if ((acptr = find_match_server(parv[1])) ||
345 ((acptr = FindClient(parv[1])) && !MyUser(acptr))) {
347 parv[2] = acptr->user->server->name;
349 parv[2] = acptr->name;
353 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
354 "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
359 /* Got "TRACE <tname> :<target>" */
361 if (MyUser(sptr) || Protocol(cptr) < 10)
362 acptr = find_match_server(parv[2]);
364 acptr = FindNServer(parv[2]);
365 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
366 parv)) == HUNTED_NOSUCH)
371 if (i == HUNTED_PASS) {
373 acptr = next_client(GlobalClientList, tname);
376 send_reply(sptr, RPL_TRACELINK,
378 version, debugmode, tname, acptr ? acptr->from->name : "<No_match>"
380 version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
381 (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0
383 ); /* I really could do without GODMODE */
387 doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : 1;
388 wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
389 dow = wilds || doall;
391 /* Don't give (long) remote listings to lusers */
392 if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
395 for (i = 0; i < MAXCONNECTIONS; i++)
396 link_s[i] = 0, link_u[i] = 0;
399 for (acptr = GlobalClientList; acptr; acptr = acptr->next) {
401 link_u[acptr->from->fd]++;
402 else if (IsServer(acptr))
403 link_s[acptr->from->fd]++;
407 /* report all direct connections */
409 for (i = 0; i <= HighestFd; i++) {
410 unsigned int conClass;
412 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
414 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
415 !IsAnOper(acptr) && (acptr != sptr))
417 if (!doall && wilds && match(tname, acptr->name))
419 if (!dow && 0 != ircd_strcmp(tname, acptr->name))
421 conClass = get_client_class(acptr);
423 switch (acptr->status) {
424 case STAT_CONNECTING:
425 send_reply(sptr, RPL_TRACECONNECTING, conClass, acptr->name);
429 send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, acptr->name);
435 case STAT_UNKNOWN_USER:
436 send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
437 get_client_name(acptr, HIDE_IP));
440 case STAT_UNKNOWN_SERVER:
441 send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
445 /* Only opers see users if there is a wildcard
446 but anyone can see all the opers. */
447 if ((IsAnOper(sptr) && (MyUser(sptr) ||
448 !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
450 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
451 get_client_name(acptr, HIDE_IP),
452 CurrentTime - acptr->lasttime);
454 send_reply(sptr, RPL_TRACEUSER, conClass,
455 get_client_name(acptr, HIDE_IP),
456 CurrentTime - acptr->lasttime);
461 * Connection is a server
463 * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
465 * class Class the server is in
466 * nS Number of servers reached via this link
467 * nC Number of clients reached via this link
468 * name Name of the server linked
469 * ConnBy Who established this link
470 * last Seconds since we got something from this link
471 * age Seconds this link has been alive
473 * Additional comments etc...... -Cym-<cym@acrux.net>
477 if (acptr->serv->user)
478 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
479 link_u[i], acptr->name,
480 (*acptr->serv->by) ? acptr->serv->by : "*",
481 acptr->serv->user->username, acptr->serv->user->host,
482 CurrentTime - acptr->lasttime,
483 CurrentTime - acptr->serv->timestamp);
485 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
486 link_u[i], acptr->name,
487 (*acptr->serv->by) ? acptr->serv->by : "*", "*",
488 me.name, CurrentTime - acptr->lasttime,
489 CurrentTime - acptr->serv->timestamp);
492 default: /* We actually shouldn't come here, -msa */
493 send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
499 * Add these lines to summarize the above which can get rather long
500 * and messy when done remotely - Avalon
502 if (!IsAnOper(sptr) || !cnt) {
504 /* let the user have some idea that its at the end of the trace */
505 send_reply(sptr, RPL_TRACESERVER, 0, link_s[me.fd],
506 link_u[me.fd], "<No_match>", *(me.serv->by) ?
507 me.serv->by : "*", "*", me.name, 0, 0);
511 for (cl = get_class_list(); cl; cl = cl->next) {
513 send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
520 * mo_trace - oper message handler
522 * parv[0] = sender prefix
523 * parv[1] = nick or servername
524 * parv[2] = 'target' servername
526 int mo_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
529 struct Client* acptr;
530 const struct ConnectionClass* cl;
533 int link_s[MAXCONNECTIONS];
534 int link_u[MAXCONNECTIONS];
539 if (parc < 2 || BadPtr(parv[1])) {
540 /* just "TRACE" without parameters. Must be from local client */
545 } else if (parc < 3 || BadPtr(parv[2])) {
546 /* No target specified. Make one before propagating. */
549 if ((acptr = find_match_server(parv[1])) ||
550 ((acptr = FindClient(parv[1])) && !MyUser(acptr))) {
552 parv[2] = acptr->user->server->name;
554 parv[2] = acptr->name;
557 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
558 "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
563 /* Got "TRACE <tname> :<target>" */
565 if (MyUser(sptr) || Protocol(cptr) < 10)
566 acptr = find_match_server(parv[2]);
568 acptr = FindNServer(parv[2]);
569 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
570 parv)) == HUNTED_NOSUCH)
575 if (i == HUNTED_PASS) {
577 acptr = next_client(GlobalClientList, tname);
580 send_reply(sptr, RPL_TRACELINK,
582 version, debugmode, tname, acptr ? acptr->from->name : "<No_match>"
584 version, debugmode, tname, acptr ? acptr->from->name : "<No_match>",
585 (acptr && acptr->from->serv) ? acptr->from->serv->timestamp : 0
587 ); /* I really could do without GODMODE */
591 doall = (parv[1] && (parc > 1)) ? !match(tname, me.name) : 1;
592 wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
593 dow = wilds || doall;
595 /* Don't give (long) remote listings to lusers */
596 if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
599 for (i = 0; i < MAXCONNECTIONS; i++)
600 link_s[i] = 0, link_u[i] = 0;
603 for (acptr = GlobalClientList; acptr; acptr = acptr->next) {
605 link_u[acptr->from->fd]++;
606 else if (IsServer(acptr))
607 link_s[acptr->from->fd]++;
611 /* report all direct connections */
613 for (i = 0; i <= HighestFd; i++) {
614 unsigned int conClass;
616 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
618 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
619 !IsAnOper(acptr) && (acptr != sptr))
621 if (!doall && wilds && match(tname, acptr->name))
623 if (!dow && 0 != ircd_strcmp(tname, acptr->name))
625 conClass = get_client_class(acptr);
627 switch (acptr->status) {
628 case STAT_CONNECTING:
629 send_reply(sptr, RPL_TRACECONNECTING, conClass, acptr->name);
633 send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, acptr->name);
639 case STAT_UNKNOWN_USER:
640 send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
641 get_client_name(acptr, HIDE_IP));
644 case STAT_UNKNOWN_SERVER:
645 send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
649 /* Only opers see users if there is a wildcard
650 but anyone can see all the opers. */
651 if ((IsAnOper(sptr) && (MyUser(sptr) ||
652 !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
654 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
655 get_client_name(acptr, HIDE_IP),
656 CurrentTime - acptr->lasttime);
658 send_reply(sptr, RPL_TRACEUSER, conClass,
659 get_client_name(acptr, HIDE_IP),
660 CurrentTime - acptr->lasttime);
665 * Connection is a server
667 * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
669 * class Class the server is in
670 * nS Number of servers reached via this link
671 * nC Number of clients reached via this link
672 * name Name of the server linked
673 * ConnBy Who established this link
674 * last Seconds since we got something from this link
675 * age Seconds this link has been alive
677 * Additional comments etc...... -Cym-<cym@acrux.net>
681 if (acptr->serv->user)
682 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
683 link_u[i], acptr->name,
684 (*acptr->serv->by) ? acptr->serv->by : "*",
685 acptr->serv->user->username, acptr->serv->user->host,
686 CurrentTime - acptr->lasttime,
687 CurrentTime - acptr->serv->timestamp);
689 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
690 link_u[i], acptr->name,
691 (*acptr->serv->by) ? acptr->serv->by : "*", "*",
692 me.name, CurrentTime - acptr->lasttime,
693 CurrentTime - acptr->serv->timestamp);
696 default: /* We actually shouldn't come here, -msa */
697 send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
703 * Add these lines to summarize the above which can get rather long
704 * and messy when done remotely - Avalon
706 if (!IsAnOper(sptr) || !cnt) {
708 /* let the user have some idea that its at the end of the trace */
709 send_reply(sptr, RPL_TRACESERVER, 0, link_s[me.fd],
710 link_u[me.fd], "<No_match>", *(me.serv->by) ?
711 me.serv->by : "*", "*", me.name, 0, 0);
715 for (cl = get_class_list(); cl; cl = cl->next) {
717 send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));