]>
Commit | Line | Data |
---|---|---|
212380e3 AC |
1 | /* |
2 | * ircd-ratbox: A slightly useful ircd. | |
3 | * m_trace.c: Traces a path to a client/server. | |
4 | * | |
5 | * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center | |
6 | * Copyright (C) 1996-2002 Hybrid Development Team | |
7 | * Copyright (C) 2002-2005 ircd-ratbox development team | |
8 | * | |
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 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
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. | |
18 | * | |
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
22 | * USA | |
212380e3 AC |
23 | */ |
24 | ||
25 | #include "stdinc.h" | |
26 | #include "class.h" | |
27 | #include "hook.h" | |
28 | #include "client.h" | |
29 | #include "hash.h" | |
212380e3 | 30 | #include "hash.h" |
4562c604 | 31 | #include "match.h" |
212380e3 AC |
32 | #include "ircd.h" |
33 | #include "numeric.h" | |
212380e3 AC |
34 | #include "s_serv.h" |
35 | #include "s_conf.h" | |
36 | #include "s_newconf.h" | |
37 | #include "send.h" | |
38 | #include "msg.h" | |
39 | #include "parse.h" | |
40 | #include "modules.h" | |
41 | ||
be9c3979 AW |
42 | static const char trace_desc[] = |
43 | "Provides the TRACE command to trace the route to a client or server"; | |
212380e3 | 44 | |
3c7d6fcc | 45 | static void m_trace(struct MsgBuf *, struct Client *, struct Client *, int, const char **); |
eeabf33a | 46 | |
212380e3 AC |
47 | static void trace_spy(struct Client *, struct Client *); |
48 | ||
49 | struct Message trace_msgtab = { | |
7baa37a9 | 50 | "TRACE", 0, 0, 0, 0, |
212380e3 AC |
51 | {mg_unreg, {m_trace, 0}, {m_trace, 0}, mg_ignore, mg_ignore, {m_trace, 0}} |
52 | }; | |
53 | ||
54 | int doing_trace_hook; | |
01fb744c | 55 | int doing_trace_show_idle_hook; |
212380e3 AC |
56 | |
57 | mapi_clist_av1 trace_clist[] = { &trace_msgtab, NULL }; | |
58 | mapi_hlist_av1 trace_hlist[] = { | |
59 | { "doing_trace", &doing_trace_hook }, | |
01fb744c | 60 | { "doing_trace_show_idle", &doing_trace_show_idle_hook }, |
212380e3 AC |
61 | { NULL, NULL } |
62 | }; | |
be9c3979 | 63 | DECLARE_MODULE_AV2(trace, NULL, NULL, trace_clist, trace_hlist, NULL, NULL, NULL, trace_desc); |
212380e3 AC |
64 | |
65 | static void count_downlinks(struct Client *server_p, int *pservcount, int *pusercount); | |
a4da8e48 | 66 | static int report_this_status(struct Client *source_p, struct Client *target_p); |
212380e3 | 67 | |
a3afc27a VY |
68 | static const char *empty_sockhost = "255.255.255.255"; |
69 | ||
212380e3 AC |
70 | /* |
71 | * m_trace | |
212380e3 AC |
72 | * parv[1] = servername |
73 | */ | |
3c7d6fcc | 74 | static void |
428ca87b | 75 | m_trace(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) |
212380e3 AC |
76 | { |
77 | struct Client *target_p = NULL; | |
78 | struct Class *cltmp; | |
79 | const char *tname; | |
3c7d6fcc EM |
80 | bool doall = false, wilds, dow; |
81 | int cnt = 0; | |
5b96d9a6 | 82 | rb_dlink_node *ptr; |
212380e3 AC |
83 | |
84 | if(parc > 1) | |
85 | { | |
86 | tname = parv[1]; | |
87 | ||
88 | if(parc > 2) | |
89 | { | |
90 | if(hunt_server(client_p, source_p, ":%s TRACE %s :%s", 2, parc, parv) != | |
91 | HUNTED_ISME) | |
3c7d6fcc | 92 | return; |
212380e3 AC |
93 | } |
94 | } | |
95 | else | |
96 | tname = me.name; | |
97 | ||
98 | /* if we have 3 parameters, then the command is directed at us. So | |
99 | * we shouldnt be forwarding it anywhere. | |
100 | */ | |
101 | if(parc < 3) | |
102 | { | |
103 | switch (hunt_server(client_p, source_p, ":%s TRACE :%s", 1, parc, parv)) | |
104 | { | |
105 | case HUNTED_PASS: /* note: gets here only if parv[1] exists */ | |
106 | { | |
107 | struct Client *ac2ptr; | |
108 | ||
109 | if(MyClient(source_p)) | |
110 | ac2ptr = find_named_client(tname); | |
111 | else | |
112 | ac2ptr = find_client(tname); | |
113 | ||
114 | if(ac2ptr == NULL) | |
115 | { | |
2fb07961 | 116 | RB_DLINK_FOREACH(ptr, global_serv_list.head) |
212380e3 AC |
117 | { |
118 | ac2ptr = ptr->data; | |
119 | ||
4d7a1ee5 | 120 | if(match(tname, ac2ptr->name)) |
212380e3 AC |
121 | break; |
122 | else | |
123 | ac2ptr = NULL; | |
124 | } | |
125 | } | |
126 | ||
127 | /* giving this out with flattened links defeats the | |
128 | * object --fl | |
129 | */ | |
d4f7eb4c | 130 | if(IsOperGeneral(source_p) || IsExemptShide(source_p) || |
212380e3 | 131 | !ConfigServerHide.flatten_links) |
55abcbb2 | 132 | sendto_one_numeric(source_p, RPL_TRACELINK, |
212380e3 | 133 | form_str(RPL_TRACELINK), |
55abcbb2 | 134 | ircd_version, |
212380e3 AC |
135 | ac2ptr ? ac2ptr->name : tname, |
136 | ac2ptr ? ac2ptr->from->name : "EEK!"); | |
137 | ||
3c7d6fcc | 138 | return; |
212380e3 AC |
139 | } |
140 | ||
141 | case HUNTED_ISME: | |
142 | break; | |
143 | ||
144 | default: | |
3c7d6fcc | 145 | return; |
212380e3 AC |
146 | } |
147 | } | |
148 | ||
149 | if(match(tname, me.name)) | |
150 | { | |
3c7d6fcc | 151 | doall = true; |
212380e3 AC |
152 | } |
153 | /* if theyre tracing our SID, we need to move tname to our name so | |
154 | * we dont give the sid in ENDOFTRACE | |
155 | */ | |
156 | else if(!MyClient(source_p) && !strcmp(tname, me.id)) | |
157 | { | |
3c7d6fcc | 158 | doall = true; |
212380e3 AC |
159 | tname = me.name; |
160 | } | |
161 | ||
162 | wilds = strchr(tname, '*') || strchr(tname, '?'); | |
163 | dow = wilds || doall; | |
164 | ||
165 | /* specific trace */ | |
3c7d6fcc | 166 | if(!dow) |
212380e3 AC |
167 | { |
168 | if(MyClient(source_p) || parc > 2) | |
169 | target_p = find_named_person(tname); | |
170 | else | |
171 | target_p = find_person(tname); | |
172 | ||
173 | /* tname could be pointing to an ID at this point, so reset | |
174 | * it to target_p->name if we have a target --fl | |
175 | */ | |
176 | if(target_p != NULL) | |
177 | { | |
a4da8e48 | 178 | report_this_status(source_p, target_p); |
212380e3 AC |
179 | tname = target_p->name; |
180 | } | |
181 | ||
182 | trace_spy(source_p, target_p); | |
183 | ||
55abcbb2 | 184 | sendto_one_numeric(source_p, RPL_ENDOFTRACE, |
212380e3 | 185 | form_str(RPL_ENDOFTRACE), tname); |
3c7d6fcc | 186 | return; |
212380e3 AC |
187 | } |
188 | ||
189 | trace_spy(source_p, NULL); | |
190 | ||
55abcbb2 | 191 | /* give non-opers a limited trace output of themselves (if local), |
212380e3 AC |
192 | * opers and servers (if no shide) --fl |
193 | */ | |
194 | if(!IsOper(source_p)) | |
195 | { | |
196 | if(MyClient(source_p)) | |
197 | { | |
198 | if(doall || (wilds && match(tname, source_p->name))) | |
a4da8e48 | 199 | report_this_status(source_p, source_p); |
212380e3 AC |
200 | } |
201 | ||
5b96d9a6 | 202 | RB_DLINK_FOREACH(ptr, local_oper_list.head) |
212380e3 AC |
203 | { |
204 | target_p = ptr->data; | |
205 | ||
206 | if(!doall && wilds && (match(tname, target_p->name) == 0)) | |
207 | continue; | |
208 | ||
1123eefc EK |
209 | if(!SeesOper(target_p, source_p)) |
210 | continue; | |
211 | ||
a4da8e48 | 212 | report_this_status(source_p, target_p); |
212380e3 AC |
213 | } |
214 | ||
215 | if (IsExemptShide(source_p) || !ConfigServerHide.flatten_links) | |
216 | { | |
5b96d9a6 | 217 | RB_DLINK_FOREACH(ptr, serv_list.head) |
212380e3 AC |
218 | { |
219 | target_p = ptr->data; | |
220 | ||
221 | if(!doall && wilds && !match(tname, target_p->name)) | |
222 | continue; | |
223 | ||
a4da8e48 | 224 | report_this_status(source_p, target_p); |
212380e3 AC |
225 | } |
226 | } | |
227 | ||
55abcbb2 | 228 | sendto_one_numeric(source_p, RPL_ENDOFTRACE, |
212380e3 | 229 | form_str(RPL_ENDOFTRACE), tname); |
3c7d6fcc | 230 | return; |
212380e3 AC |
231 | } |
232 | ||
233 | /* source_p is opered */ | |
234 | ||
235 | /* report all direct connections */ | |
5b96d9a6 | 236 | RB_DLINK_FOREACH(ptr, lclient_list.head) |
212380e3 AC |
237 | { |
238 | target_p = ptr->data; | |
239 | ||
240 | /* dont show invisible users to remote opers */ | |
1123eefc | 241 | if(IsInvisible(target_p) && dow && !MyConnect(source_p) && !SeesOper(target_p, source_p)) |
212380e3 AC |
242 | continue; |
243 | ||
244 | if(!doall && wilds && !match(tname, target_p->name)) | |
245 | continue; | |
246 | ||
abee738b | 247 | /* remote opers may not see invisible normal users */ |
1123eefc | 248 | if(dow && !MyConnect(source_p) && !SeesOper(target_p, source_p) && |
abee738b JT |
249 | IsInvisible(target_p)) |
250 | continue; | |
251 | ||
a4da8e48 | 252 | cnt = report_this_status(source_p, target_p); |
212380e3 AC |
253 | } |
254 | ||
5b96d9a6 | 255 | RB_DLINK_FOREACH(ptr, serv_list.head) |
212380e3 AC |
256 | { |
257 | target_p = ptr->data; | |
258 | ||
259 | if(!doall && wilds && !match(tname, target_p->name)) | |
260 | continue; | |
261 | ||
a4da8e48 | 262 | cnt = report_this_status(source_p, target_p); |
212380e3 AC |
263 | } |
264 | ||
265 | if(MyConnect(source_p)) | |
266 | { | |
5b96d9a6 | 267 | RB_DLINK_FOREACH(ptr, unknown_list.head) |
212380e3 AC |
268 | { |
269 | target_p = ptr->data; | |
270 | ||
271 | if(!doall && wilds && !match(tname, target_p->name)) | |
272 | continue; | |
273 | ||
a4da8e48 | 274 | cnt = report_this_status(source_p, target_p); |
212380e3 AC |
275 | } |
276 | } | |
277 | ||
278 | if(!cnt) | |
279 | { | |
280 | sendto_one_numeric(source_p, ERR_NOSUCHSERVER, form_str(ERR_NOSUCHSERVER), | |
281 | tname); | |
282 | ||
283 | /* let the user have some idea that its at the end of the | |
284 | * trace | |
285 | */ | |
55abcbb2 | 286 | sendto_one_numeric(source_p, RPL_ENDOFTRACE, |
212380e3 | 287 | form_str(RPL_ENDOFTRACE), tname); |
3c7d6fcc | 288 | return; |
212380e3 AC |
289 | } |
290 | ||
291 | if(doall) | |
292 | { | |
5b96d9a6 | 293 | RB_DLINK_FOREACH(ptr, class_list.head) |
212380e3 AC |
294 | { |
295 | cltmp = ptr->data; | |
296 | ||
297 | if(CurrUsers(cltmp) > 0) | |
298 | sendto_one_numeric(source_p, RPL_TRACECLASS, | |
55abcbb2 | 299 | form_str(RPL_TRACECLASS), |
212380e3 AC |
300 | ClassName(cltmp), CurrUsers(cltmp)); |
301 | } | |
302 | } | |
303 | ||
304 | sendto_one_numeric(source_p, RPL_ENDOFTRACE, form_str(RPL_ENDOFTRACE), tname); | |
212380e3 AC |
305 | } |
306 | ||
307 | /* | |
308 | * count_downlinks | |
309 | * | |
310 | * inputs - pointer to server to count | |
311 | * - pointers to server and user count | |
312 | * output - NONE | |
313 | * side effects - server and user counts are added to given values | |
314 | */ | |
315 | static void | |
316 | count_downlinks(struct Client *server_p, int *pservcount, int *pusercount) | |
317 | { | |
5b96d9a6 | 318 | rb_dlink_node *ptr; |
212380e3 AC |
319 | |
320 | (*pservcount)++; | |
5b96d9a6 AC |
321 | *pusercount += rb_dlink_list_length(&server_p->serv->users); |
322 | RB_DLINK_FOREACH(ptr, server_p->serv->servers.head) | |
212380e3 AC |
323 | { |
324 | count_downlinks(ptr->data, pservcount, pusercount); | |
325 | } | |
326 | } | |
327 | ||
328 | /* | |
329 | * report_this_status | |
330 | * | |
331 | * inputs - pointer to client to report to | |
332 | * - pointer to client to report about | |
333 | * output - counter of number of hits | |
334 | * side effects - NONE | |
335 | */ | |
336 | static int | |
a4da8e48 | 337 | report_this_status(struct Client *source_p, struct Client *target_p) |
212380e3 AC |
338 | { |
339 | const char *name; | |
340 | const char *class_name; | |
341 | char ip[HOSTIPLEN]; | |
342 | int cnt = 0; | |
343 | ||
344 | /* sanity check - should never happen */ | |
345 | if(!MyConnect(target_p)) | |
346 | return 0; | |
347 | ||
caa4d9d2 | 348 | rb_inet_ntop_sock((struct sockaddr *)&target_p->localClient->ip, ip, sizeof(ip)); |
212380e3 AC |
349 | class_name = get_client_class(target_p); |
350 | ||
351 | if(IsAnyServer(target_p)) | |
b3ebc7ab | 352 | name = target_p->name; |
212380e3 AC |
353 | else |
354 | name = get_client_name(target_p, HIDE_IP); | |
355 | ||
356 | switch (target_p->status) | |
357 | { | |
358 | case STAT_CONNECTING: | |
359 | sendto_one_numeric(source_p, RPL_TRACECONNECTING, | |
360 | form_str(RPL_TRACECONNECTING), | |
361 | class_name, name); | |
362 | cnt++; | |
363 | break; | |
364 | ||
365 | case STAT_HANDSHAKE: | |
366 | sendto_one_numeric(source_p, RPL_TRACEHANDSHAKE, | |
367 | form_str(RPL_TRACEHANDSHAKE), | |
368 | class_name, name); | |
369 | cnt++; | |
370 | break; | |
371 | ||
372 | case STAT_ME: | |
373 | break; | |
374 | ||
375 | case STAT_UNKNOWN: | |
376 | /* added time -Taner */ | |
377 | sendto_one_numeric(source_p, RPL_TRACEUNKNOWN, | |
378 | form_str(RPL_TRACEUNKNOWN), | |
379 | class_name, name, ip, | |
77910830 | 380 | (unsigned long)(rb_current_time() - target_p->localClient->firsttime)); |
212380e3 AC |
381 | cnt++; |
382 | break; | |
383 | ||
384 | case STAT_CLIENT: | |
a672fbb7 | 385 | { |
01fb744c DS |
386 | /* fire the doing_trace_show_idle hook to allow modules to tell us whether to show the idle time */ |
387 | hook_data_client_approval hdata_showidle; | |
388 | ||
389 | hdata_showidle.client = source_p; | |
390 | hdata_showidle.target = target_p; | |
391 | hdata_showidle.approved = WHOIS_IDLE_SHOW; | |
392 | ||
393 | call_hook(doing_trace_show_idle_hook, &hdata_showidle); | |
394 | ||
6f7b36d5 | 395 | sendto_one_numeric(source_p, |
1123eefc EK |
396 | SeesOper(target_p, source_p) ? RPL_TRACEOPERATOR : RPL_TRACEUSER, |
397 | SeesOper(target_p, source_p) ? form_str(RPL_TRACEOPERATOR) : form_str(RPL_TRACEUSER), | |
a672fbb7 JT |
398 | class_name, name, |
399 | show_ip(source_p, target_p) ? ip : empty_sockhost, | |
01fb744c DS |
400 | hdata_showidle.approved ? (unsigned long)(rb_current_time() - target_p->localClient->lasttime) : 0, |
401 | hdata_showidle.approved ? (unsigned long)(rb_current_time() - target_p->localClient->last) : 0); | |
a672fbb7 JT |
402 | |
403 | cnt++; | |
404 | } | |
a4da8e48 | 405 | break; |
212380e3 AC |
406 | |
407 | case STAT_SERVER: | |
408 | { | |
409 | int usercount = 0; | |
410 | int servcount = 0; | |
411 | ||
412 | count_downlinks(target_p, &servcount, &usercount); | |
413 | ||
414 | sendto_one_numeric(source_p, RPL_TRACESERVER, form_str(RPL_TRACESERVER), | |
415 | class_name, servcount, usercount, name, | |
416 | *(target_p->serv->by) ? target_p->serv->by : "*", "*", | |
0cce01d3 JT |
417 | me.name, |
418 | (unsigned long)(rb_current_time() - target_p->localClient->lasttime)); | |
212380e3 AC |
419 | cnt++; |
420 | ||
421 | } | |
422 | break; | |
423 | ||
424 | default: /* ...we actually shouldn't come here... --msa */ | |
55abcbb2 | 425 | sendto_one_numeric(source_p, RPL_TRACENEWTYPE, |
0cce01d3 | 426 | form_str(RPL_TRACENEWTYPE), name); |
212380e3 AC |
427 | cnt++; |
428 | break; | |
429 | } | |
430 | ||
431 | return (cnt); | |
432 | } | |
433 | ||
434 | /* trace_spy() | |
435 | * | |
436 | * input - pointer to client | |
437 | * output - none | |
438 | * side effects - hook event doing_trace is called | |
439 | */ | |
440 | static void | |
441 | trace_spy(struct Client *source_p, struct Client *target_p) | |
442 | { | |
443 | hook_data_client hdata; | |
444 | ||
445 | hdata.client = source_p; | |
446 | hdata.target = target_p; | |
447 | ||
448 | call_hook(doing_trace_hook, &hdata); | |
449 | } |