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 */
133 tname = cli_name(&me);
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] = cli_name(cli_user(acptr)->server);
144 parv[2] = cli_name(acptr);
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);
169 acptr = cli_from(acptr);
170 send_reply(sptr, RPL_TRACELINK,
171 version, debugmode, tname,
172 acptr ? cli_name(cli_from(acptr)) : "<No_match>");
176 doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1;
177 wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
178 dow = wilds || doall;
180 /* Don't give (long) remote listings to lusers */
181 if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
184 for (i = 0; i < MAXCONNECTIONS; i++)
185 link_s[i] = 0, link_u[i] = 0;
188 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
190 link_u[cli_fd(cli_from(acptr))]++;
191 else if (IsServer(acptr))
192 link_s[cli_fd(cli_from(acptr))]++;
196 /* report all direct connections */
198 for (i = 0; i <= HighestFd; i++) {
199 unsigned int conClass;
201 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
203 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
204 !IsAnOper(acptr) && (acptr != sptr))
206 if (!doall && wilds && match(tname, cli_name(acptr)))
208 if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr)))
211 conClass = get_client_class(acptr);
213 switch (cli_status(acptr)) {
214 case STAT_CONNECTING:
215 send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr));
219 send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr));
225 case STAT_UNKNOWN_USER:
226 send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
227 get_client_name(acptr, HIDE_IP));
230 case STAT_UNKNOWN_SERVER:
231 send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
235 /* Only opers see users if there is a wildcard
236 but anyone can see all the opers. */
237 if ((IsAnOper(sptr) && (MyUser(sptr) ||
238 !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
240 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
241 get_client_name(acptr, HIDE_IP),
242 CurrentTime - cli_lasttime(acptr));
244 send_reply(sptr, RPL_TRACEUSER, conClass,
245 get_client_name(acptr, HIDE_IP),
246 CurrentTime - cli_lasttime(acptr));
251 * Connection is a server
253 * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
255 * class Class the server is in
256 * nS Number of servers reached via this link
257 * nC Number of clients reached via this link
258 * name Name of the server linked
259 * ConnBy Who established this link
260 * last Seconds since we got something from this link
261 * age Seconds this link has been alive
263 * Additional comments etc...... -Cym-<cym@acrux.net>
267 if (cli_serv(acptr)->user)
268 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
269 link_u[i], cli_name(acptr),
270 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*",
271 cli_serv(acptr)->user->username, cli_serv(acptr)->user->host,
272 CurrentTime - cli_lasttime(acptr),
273 CurrentTime - cli_serv(acptr)->timestamp);
275 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
276 link_u[i], cli_name(acptr),
277 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*",
278 cli_name(&me), CurrentTime - cli_lasttime(acptr),
279 CurrentTime - cli_serv(acptr)->timestamp);
282 default: /* We actually shouldn't come here, -msa */
283 send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
289 * Add these lines to summarize the above which can get rather long
290 * and messy when done remotely - Avalon
292 if (!IsAnOper(sptr) || !cnt) {
294 /* let the user have some idea that its at the end of the trace */
295 send_reply(sptr, RPL_TRACESERVER, 0, link_s[cli_fd(&me)],
296 link_u[cli_fd(&me)], "<No_match>", *(cli_serv(&me)->by) ?
297 cli_serv(&me)->by : "*", "*", cli_name(&me), 0, 0);
301 for (cl = get_class_list(); cl; cl = cl->next) {
303 send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
310 * ms_trace - server message handler
312 * parv[0] = sender prefix
313 * parv[1] = nick or servername
314 * parv[2] = 'target' servername
316 int ms_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
319 struct Client *acptr;
320 const struct ConnectionClass* cl;
323 int link_s[MAXCONNECTIONS];
324 int link_u[MAXCONNECTIONS];
329 if (parc < 2 || BadPtr(parv[1])) {
330 /* just "TRACE" without parameters. Must be from local client */
333 tname = cli_name(&me);
335 } else if (parc < 3 || BadPtr(parv[2])) {
336 /* No target specified. Make one before propagating. */
339 if ((acptr = find_match_server(parv[1])) ||
340 ((acptr = FindClient(parv[1])) && !MyUser(acptr))) {
342 parv[2] = cli_name(cli_user(acptr)->server);
344 parv[2] = cli_name(acptr);
348 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
349 "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
354 /* Got "TRACE <tname> :<target>" */
356 if (MyUser(sptr) || Protocol(cptr) < 10)
357 acptr = find_match_server(parv[2]);
359 acptr = FindNServer(parv[2]);
360 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
361 parv)) == HUNTED_NOSUCH)
366 if (i == HUNTED_PASS) {
368 acptr = next_client(GlobalClientList, tname);
370 acptr = cli_from(acptr);
371 send_reply(sptr, RPL_TRACELINK,
372 version, debugmode, tname,
373 acptr ? cli_name(cli_from(acptr)) : "<No_match>");
377 doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1;
378 wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
379 dow = wilds || doall;
381 /* Don't give (long) remote listings to lusers */
382 if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
385 for (i = 0; i < MAXCONNECTIONS; i++)
386 link_s[i] = 0, link_u[i] = 0;
389 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
391 link_u[cli_fd(cli_from(acptr))]++;
392 else if (IsServer(acptr))
393 link_s[cli_fd(cli_from(acptr))]++;
397 /* report all direct connections */
399 for (i = 0; i <= HighestFd; i++) {
400 unsigned int conClass;
402 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
404 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
405 !IsAnOper(acptr) && (acptr != sptr))
407 if (!doall && wilds && match(tname, cli_name(acptr)))
409 if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr)))
411 conClass = get_client_class(acptr);
413 switch (cli_status(acptr)) {
414 case STAT_CONNECTING:
415 send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr));
419 send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr));
425 case STAT_UNKNOWN_USER:
426 send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
427 get_client_name(acptr, HIDE_IP));
430 case STAT_UNKNOWN_SERVER:
431 send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
435 /* Only opers see users if there is a wildcard
436 but anyone can see all the opers. */
437 if ((IsAnOper(sptr) && (MyUser(sptr) ||
438 !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
440 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
441 get_client_name(acptr, HIDE_IP),
442 CurrentTime - cli_lasttime(acptr));
444 send_reply(sptr, RPL_TRACEUSER, conClass,
445 get_client_name(acptr, HIDE_IP),
446 CurrentTime - cli_lasttime(acptr));
451 * Connection is a server
453 * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
455 * class Class the server is in
456 * nS Number of servers reached via this link
457 * nC Number of clients reached via this link
458 * name Name of the server linked
459 * ConnBy Who established this link
460 * last Seconds since we got something from this link
461 * age Seconds this link has been alive
463 * Additional comments etc...... -Cym-<cym@acrux.net>
467 if (cli_serv(acptr)->user)
468 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
469 link_u[i], cli_name(acptr),
470 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*",
471 cli_serv(acptr)->user->username, cli_serv(acptr)->user->host,
472 CurrentTime - cli_lasttime(acptr),
473 CurrentTime - cli_serv(acptr)->timestamp);
475 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
476 link_u[i], cli_name(acptr),
477 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*",
478 cli_name(&me), CurrentTime - cli_lasttime(acptr),
479 CurrentTime - cli_serv(acptr)->timestamp);
482 default: /* We actually shouldn't come here, -msa */
483 send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
489 * Add these lines to summarize the above which can get rather long
490 * and messy when done remotely - Avalon
492 if (!IsAnOper(sptr) || !cnt) {
494 /* let the user have some idea that its at the end of the trace */
495 send_reply(sptr, RPL_TRACESERVER, 0, link_s[cli_fd(&me)],
496 link_u[cli_fd(&me)], "<No_match>", *(cli_serv(&me)->by) ?
497 cli_serv(&me)->by : "*", "*", cli_name(&me), 0, 0);
501 for (cl = get_class_list(); cl; cl = cl->next) {
503 send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));
510 * mo_trace - oper message handler
512 * parv[0] = sender prefix
513 * parv[1] = nick or servername
514 * parv[2] = 'target' servername
516 int mo_trace(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
519 struct Client* acptr;
520 const struct ConnectionClass* cl;
523 int link_s[MAXCONNECTIONS];
524 int link_u[MAXCONNECTIONS];
529 if (parc < 2 || BadPtr(parv[1])) {
530 /* just "TRACE" without parameters. Must be from local client */
533 tname = cli_name(&me);
535 } else if (parc < 3 || BadPtr(parv[2])) {
536 /* No target specified. Make one before propagating. */
539 if ((acptr = find_match_server(parv[1])) ||
540 ((acptr = FindClient(parv[1])) && !MyUser(acptr))) {
542 parv[2] = cli_name(cli_user(acptr)->server);
544 parv[2] = cli_name(acptr);
547 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
548 "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
553 /* Got "TRACE <tname> :<target>" */
555 if (MyUser(sptr) || Protocol(cptr) < 10)
556 acptr = find_match_server(parv[2]);
558 acptr = FindNServer(parv[2]);
559 if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
560 parv)) == HUNTED_NOSUCH)
565 if (i == HUNTED_PASS) {
567 acptr = next_client(GlobalClientList, tname);
569 acptr = cli_from(acptr);
570 send_reply(sptr, RPL_TRACELINK,
571 version, debugmode, tname,
572 acptr ? cli_name(cli_from(acptr)) : "<No_match>");
576 doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1;
577 wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
578 dow = wilds || doall;
580 /* Don't give (long) remote listings to lusers */
581 if (dow && !MyConnect(sptr) && !IsAnOper(sptr))
584 for (i = 0; i < MAXCONNECTIONS; i++)
585 link_s[i] = 0, link_u[i] = 0;
588 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
590 link_u[cli_fd(cli_from(acptr))]++;
591 else if (IsServer(acptr))
592 link_s[cli_fd(cli_from(acptr))]++;
596 /* report all direct connections */
598 for (i = 0; i <= HighestFd; i++) {
599 unsigned int conClass;
601 if (!(acptr = LocalClientArray[i])) /* Local Connection? */
603 if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
604 !IsAnOper(acptr) && (acptr != sptr))
606 if (!doall && wilds && match(tname, cli_name(acptr)))
608 if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr)))
610 conClass = get_client_class(acptr);
612 switch (cli_status(acptr)) {
613 case STAT_CONNECTING:
614 send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr));
618 send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr));
624 case STAT_UNKNOWN_USER:
625 send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
626 get_client_name(acptr, HIDE_IP));
629 case STAT_UNKNOWN_SERVER:
630 send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
634 /* Only opers see users if there is a wildcard
635 but anyone can see all the opers. */
636 if ((IsAnOper(sptr) && (MyUser(sptr) ||
637 !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
639 send_reply(sptr, RPL_TRACEOPERATOR, conClass,
640 get_client_name(acptr, HIDE_IP),
641 CurrentTime - cli_lasttime(acptr));
643 send_reply(sptr, RPL_TRACEUSER, conClass,
644 get_client_name(acptr, HIDE_IP),
645 CurrentTime - cli_lasttime(acptr));
650 * Connection is a server
652 * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
654 * class Class the server is in
655 * nS Number of servers reached via this link
656 * nC Number of clients reached via this link
657 * name Name of the server linked
658 * ConnBy Who established this link
659 * last Seconds since we got something from this link
660 * age Seconds this link has been alive
662 * Additional comments etc...... -Cym-<cym@acrux.net>
666 if (cli_serv(acptr)->user)
667 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
668 link_u[i], cli_name(acptr),
669 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*",
670 cli_serv(acptr)->user->username, cli_serv(acptr)->user->host,
671 CurrentTime - cli_lasttime(acptr),
672 CurrentTime - cli_serv(acptr)->timestamp);
674 send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
675 link_u[i], cli_name(acptr),
676 (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*",
677 cli_name(&me), CurrentTime - cli_lasttime(acptr),
678 CurrentTime - cli_serv(acptr)->timestamp);
681 default: /* We actually shouldn't come here, -msa */
682 send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
688 * Add these lines to summarize the above which can get rather long
689 * and messy when done remotely - Avalon
691 if (!IsAnOper(sptr) || !cnt) {
693 /* let the user have some idea that its at the end of the trace */
694 send_reply(sptr, RPL_TRACESERVER, 0, link_s[cli_fd(&me)],
695 link_u[cli_fd(&me)], "<No_match>", *(cli_serv(&me)->by) ?
696 cli_serv(&me)->by : "*", "*", cli_name(&me), 0, 0);
700 for (cl = get_class_list(); cl; cl = cl->next) {
702 send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl));