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
88 #include "ircd_reply.h"
89 #include "ircd_string.h"
104 * m_trace - generic message handler
106 * parv[0] = sender prefix
107 * parv[1] = nick or servername
108 * parv[2] = 'target' servername
110 int m_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
113 struct Client *acptr;
114 const struct ConnectionClass* cl;
117 int link_s[MAXCONNECTIONS];
118 int link_u[MAXCONNECTIONS];
123 if (parc < 2 || BadPtr(parv[1])) {
124 /* just "TRACE" without parameters. Must be from local client */
127 tname = cli_name(&me);
129 } else if (parc < 3 || BadPtr(parv[2])) {
130 /* No target specified. Make one before propagating. */
133 if ((acptr = find_match_server(parv[1])) ||
134 ((acptr = FindClient(parv[1])) && !MyUser(acptr))) {
136 parv[2] = cli_name(cli_user(acptr)->server);
138 parv[2] = cli_name(acptr);
141 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
142 "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
147 /* Got "TRACE <tname> :<target>" */
149 if (MyUser(sptr) || Protocol(cptr) < 10)
150 acptr = find_match_server(parv[2]);
152 acptr = FindNServer(parv[2]);
153 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
154 parv)) == HUNTED_NOSUCH)
159 if (i == HUNTED_PASS) {
161 acptr = next_client(GlobalClientList, tname);
163 acptr = cli_from(acptr);
164 send_reply(sptr, RPL_TRACELINK,
165 version, debugmode, tname,
166 acptr ? cli_name(cli_from(acptr)) : "<No_match>");
170 doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1;
171 wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
172 dow = wilds || doall;
174 /* Don't give (long) remote listings to lusers */
175 if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
178 for (i = 0; i < MAXCONNECTIONS; i++)
179 link_s[i] = 0, link_u[i] = 0;
182 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
184 link_u[cli_fd(cli_from(acptr))]++;
185 else if (IsServer(acptr))
186 link_s[cli_fd(cli_from(acptr))]++;
190 /* report all direct connections */
192 for (i = 0; i <= HighestFd; i++) {
193 const char *conClass;
195 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
197 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
198 !IsAnOper(acptr) && (acptr != sptr))
200 if (!doall && wilds && match(tname, cli_name(acptr)))
202 if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr)))
205 conClass = get_client_class(acptr);
207 switch (cli_status(acptr)) {
208 case STAT_CONNECTING:
209 send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr));
213 send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr));
219 case STAT_UNKNOWN_USER:
220 send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
221 get_client_name(acptr, HIDE_IP));
224 case STAT_UNKNOWN_SERVER:
225 send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
229 /* Only opers see users if there is a wildcard
230 but anyone can see all the opers. */
231 if ((IsAnOper(sptr) && (MyUser(sptr) ||
232 !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
234 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
235 get_client_name(acptr, SHOW_IP),
236 CurrentTime - cli_lasttime(acptr));
238 send_reply(sptr, RPL_TRACEUSER, conClass,
239 get_client_name(acptr, SHOW_IP),
240 CurrentTime - cli_lasttime(acptr));
245 * Connection is a server
247 * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
249 * class Class the server is in
250 * nS Number of servers reached via this link
251 * nC Number of clients reached via this link
252 * name Name of the server linked
253 * ConnBy Who established this link
254 * last Seconds since we got something from this link
255 * age Seconds this link has been alive
257 * Additional comments etc...... -Cym-<cym@acrux.net>
261 if (cli_serv(acptr)->user)
262 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
263 link_u[i], cli_name(acptr),
264 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*",
265 cli_serv(acptr)->user->username, cli_serv(acptr)->user->host,
266 CurrentTime - cli_lasttime(acptr),
267 CurrentTime - cli_serv(acptr)->timestamp);
269 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
270 link_u[i], cli_name(acptr),
271 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*",
272 cli_name(&me), CurrentTime - cli_lasttime(acptr),
273 CurrentTime - cli_serv(acptr)->timestamp);
276 default: /* We actually shouldn't come here, -msa */
277 send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
283 * Add these lines to summarize the above which can get rather long
284 * and messy when done remotely - Avalon
286 if (!IsAnOper(sptr) || !cnt) {
288 /* let the user have some idea that its at the end of the trace */
289 send_reply(sptr, RPL_TRACESERVER, 0, link_s[cli_fd(&me)],
290 link_u[cli_fd(&me)], "<No_match>", *(cli_serv(&me)->by) ?
291 cli_serv(&me)->by : "*", "*", cli_name(&me), 0, 0);
295 for (cl = get_class_list(); cl; cl = cl->next) {
297 send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
304 * ms_trace - server message handler
306 * parv[0] = sender prefix
307 * parv[1] = nick or servername
308 * parv[2] = 'target' servername
310 int ms_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
313 struct Client *acptr;
314 const struct ConnectionClass* cl;
317 int link_s[MAXCONNECTIONS];
318 int link_u[MAXCONNECTIONS];
323 if (parc < 2 || BadPtr(parv[1])) {
324 /* just "TRACE" without parameters. Must be from local client */
327 tname = cli_name(&me);
329 } else if (parc < 3 || BadPtr(parv[2])) {
330 /* No target specified. Make one before propagating. */
333 if ((acptr = find_match_server(parv[1])) ||
334 ((acptr = FindClient(parv[1])) && !MyUser(acptr))) {
336 parv[2] = cli_name(cli_user(acptr)->server);
338 parv[2] = cli_name(acptr);
342 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
343 "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
348 /* Got "TRACE <tname> :<target>" */
350 if (MyUser(sptr) || Protocol(cptr) < 10)
351 acptr = find_match_server(parv[2]);
353 acptr = FindNServer(parv[2]);
354 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
355 parv)) == HUNTED_NOSUCH)
360 if (i == HUNTED_PASS) {
362 acptr = next_client(GlobalClientList, tname);
364 acptr = cli_from(acptr);
365 send_reply(sptr, RPL_TRACELINK,
366 version, debugmode, tname,
367 acptr ? cli_name(cli_from(acptr)) : "<No_match>");
371 doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1;
372 wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
373 dow = wilds || doall;
375 /* Don't give (long) remote listings to lusers */
376 if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
379 for (i = 0; i < MAXCONNECTIONS; i++)
380 link_s[i] = 0, link_u[i] = 0;
383 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
385 link_u[cli_fd(cli_from(acptr))]++;
386 else if (IsServer(acptr))
387 link_s[cli_fd(cli_from(acptr))]++;
391 /* report all direct connections */
393 for (i = 0; i <= HighestFd; i++) {
394 const char *conClass;
396 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
398 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
399 !IsAnOper(acptr) && (acptr != sptr))
401 if (!doall && wilds && match(tname, cli_name(acptr)))
403 if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr)))
405 conClass = get_client_class(acptr);
407 switch (cli_status(acptr)) {
408 case STAT_CONNECTING:
409 send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr));
413 send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr));
419 case STAT_UNKNOWN_USER:
420 send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
421 get_client_name(acptr, HIDE_IP));
424 case STAT_UNKNOWN_SERVER:
425 send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
429 /* Only opers see users if there is a wildcard
430 but anyone can see all the opers. */
431 if ((IsAnOper(sptr) && (MyUser(sptr) ||
432 !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
434 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
435 get_client_name(acptr, SHOW_IP),
436 CurrentTime - cli_lasttime(acptr));
438 send_reply(sptr, RPL_TRACEUSER, conClass,
439 get_client_name(acptr, SHOW_IP),
440 CurrentTime - cli_lasttime(acptr));
445 * Connection is a server
447 * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
449 * class Class the server is in
450 * nS Number of servers reached via this link
451 * nC Number of clients reached via this link
452 * name Name of the server linked
453 * ConnBy Who established this link
454 * last Seconds since we got something from this link
455 * age Seconds this link has been alive
457 * Additional comments etc...... -Cym-<cym@acrux.net>
461 if (cli_serv(acptr)->user)
462 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
463 link_u[i], cli_name(acptr),
464 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*",
465 cli_serv(acptr)->user->username, cli_serv(acptr)->user->host,
466 CurrentTime - cli_lasttime(acptr),
467 CurrentTime - cli_serv(acptr)->timestamp);
469 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
470 link_u[i], cli_name(acptr),
471 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*",
472 cli_name(&me), CurrentTime - cli_lasttime(acptr),
473 CurrentTime - cli_serv(acptr)->timestamp);
476 default: /* We actually shouldn't come here, -msa */
477 send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
483 * Add these lines to summarize the above which can get rather long
484 * and messy when done remotely - Avalon
486 if (!IsAnOper(sptr) || !cnt) {
488 /* let the user have some idea that its at the end of the trace */
489 send_reply(sptr, RPL_TRACESERVER, 0, link_s[cli_fd(&me)],
490 link_u[cli_fd(&me)], "<No_match>", *(cli_serv(&me)->by) ?
491 cli_serv(&me)->by : "*", "*", cli_name(&me), 0, 0);
495 for (cl = get_class_list(); cl; cl = cl->next) {
497 send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
504 * mo_trace - oper message handler
506 * parv[0] = sender prefix
507 * parv[1] = nick or servername
508 * parv[2] = 'target' servername
510 int mo_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
513 struct Client* acptr;
514 const struct ConnectionClass* cl;
517 int link_s[MAXCONNECTIONS];
518 int link_u[MAXCONNECTIONS];
523 if (parc < 2 || BadPtr(parv[1])) {
524 /* just "TRACE" without parameters. Must be from local client */
527 tname = cli_name(&me);
529 } else if (parc < 3 || BadPtr(parv[2])) {
530 /* No target specified. Make one before propagating. */
533 if ((acptr = find_match_server(parv[1])) ||
534 ((acptr = FindClient(parv[1])) && !MyUser(acptr))) {
536 parv[2] = cli_name(cli_user(acptr)->server);
538 parv[2] = cli_name(acptr);
541 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
542 "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
547 /* Got "TRACE <tname> :<target>" */
549 if (MyUser(sptr) || Protocol(cptr) < 10)
550 acptr = find_match_server(parv[2]);
552 acptr = FindNServer(parv[2]);
553 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
554 parv)) == HUNTED_NOSUCH)
559 if (i == HUNTED_PASS) {
561 acptr = next_client(GlobalClientList, tname);
563 acptr = cli_from(acptr);
564 send_reply(sptr, RPL_TRACELINK,
565 version, debugmode, tname,
566 acptr ? cli_name(cli_from(acptr)) : "<No_match>");
570 doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1;
571 wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
572 dow = wilds || doall;
574 /* Don't give (long) remote listings to lusers */
575 if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
578 for (i = 0; i < MAXCONNECTIONS; i++)
579 link_s[i] = 0, link_u[i] = 0;
582 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
584 link_u[cli_fd(cli_from(acptr))]++;
585 else if (IsServer(acptr))
586 link_s[cli_fd(cli_from(acptr))]++;
590 /* report all direct connections */
592 for (i = 0; i <= HighestFd; i++) {
593 const char *conClass;
595 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
597 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
598 !IsAnOper(acptr) && (acptr != sptr))
600 if (!doall && wilds && match(tname, cli_name(acptr)))
602 if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr)))
604 conClass = get_client_class(acptr);
606 switch (cli_status(acptr)) {
607 case STAT_CONNECTING:
608 send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr));
612 send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr));
618 case STAT_UNKNOWN_USER:
619 send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
620 get_client_name(acptr, HIDE_IP));
623 case STAT_UNKNOWN_SERVER:
624 send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
628 /* Only opers see users if there is a wildcard
629 but anyone can see all the opers. */
630 if ((IsAnOper(sptr) && (MyUser(sptr) ||
631 !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
633 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
634 get_client_name(acptr, SHOW_IP),
635 CurrentTime - cli_lasttime(acptr));
637 send_reply(sptr, RPL_TRACEUSER, conClass,
638 get_client_name(acptr, SHOW_IP),
639 CurrentTime - cli_lasttime(acptr));
644 * Connection is a server
646 * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
648 * class Class the server is in
649 * nS Number of servers reached via this link
650 * nC Number of clients reached via this link
651 * name Name of the server linked
652 * ConnBy Who established this link
653 * last Seconds since we got something from this link
654 * age Seconds this link has been alive
656 * Additional comments etc...... -Cym-<cym@acrux.net>
660 if (cli_serv(acptr)->user)
661 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
662 link_u[i], cli_name(acptr),
663 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*",
664 cli_serv(acptr)->user->username, cli_serv(acptr)->user->host,
665 CurrentTime - cli_lasttime(acptr),
666 CurrentTime - cli_serv(acptr)->timestamp);
668 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
669 link_u[i], cli_name(acptr),
670 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*",
671 cli_name(&me), CurrentTime - cli_lasttime(acptr),
672 CurrentTime - cli_serv(acptr)->timestamp);
675 default: /* We actually shouldn't come here, -msa */
676 send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
682 * Add these lines to summarize the above which can get rather long
683 * and messy when done remotely - Avalon
685 if (!IsAnOper(sptr) || !cnt) {
687 /* let the user have some idea that its at the end of the trace */
688 send_reply(sptr, RPL_TRACESERVER, 0, link_s[cli_fd(&me)],
689 link_u[cli_fd(&me)], "<No_match>", *(cli_serv(&me)->by) ?
690 cli_serv(&me)->by : "*", "*", cli_name(&me), 0, 0);
694 for (cl = get_class_list(); cl; cl = cl->next) {
696 send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));