1 |
/* Copyright (C) 2003, 2004 Stephane Thiell |
2 |
* |
3 |
* This file is part of pxyservd (from pxys) |
4 |
* |
5 |
* This program is free software; you can redistribute it and/or |
6 |
* modify it under the terms of the GNU General Public License |
7 |
* as published by the Free Software Foundation; either version 2 |
8 |
* of the License, or (at your option) any later version. |
9 |
* |
10 |
* This program is distributed in the hope that it will be useful, |
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
* GNU General Public License for more details. |
14 |
* |
15 |
* You should have received a copy of the GNU General Public License |
16 |
* along with this program; if not, write to the Free Software |
17 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
18 |
* |
19 |
*/ |
20 |
#define RCSID "$Id: scan.c,v 1.7 2004/01/12 12:31:03 mbuna Exp $" |
21 |
|
22 |
#ifdef HAVE_CONFIG_H |
23 |
#include "config.h" |
24 |
#endif |
25 |
|
26 |
#ifndef IP_L1_CACHE_SIZE |
27 |
#define IP_L1_CACHE_SIZE 10000 |
28 |
#endif |
29 |
#ifndef IP_L1_CACHE_EXPIRE_DELAY |
30 |
#define IP_L1_CACHE_EXPIRE_DELAY 21600 |
31 |
#endif |
32 |
|
33 |
#define IP4_L1_CACHE_SIZE IP_L1_CACHE_SIZE |
34 |
#define IP6_L1_CACHE_SIZE IP_L1_CACHE_SIZE |
35 |
|
36 |
/* SCAN_TIMER_DELAY defines the delay of which pxyservd will check if |
37 |
* some queries have failed (timeout) in order to retry. |
38 |
*/ |
39 |
#define SCAN_TIMER_DELAY 10 |
40 |
|
41 |
/* SCAN_QUERY_TIMEOUT defines a minimal timeout delay in seconds for a query. |
42 |
* A query won't be resent until this time is elapsed. |
43 |
* Should not be too low, or pxyscand will receive each time |
44 |
* several queries for the same IP, for example. |
45 |
* Shouldn't be too high, or it will alter the reactivity, when for |
46 |
* example, pxyscand restarts. |
47 |
* 3-5 minutes is probably a good value. |
48 |
*/ |
49 |
#define SCAN_QUERY_TIMEOUT 200 |
50 |
|
51 |
#include <assert.h> |
52 |
#include <errno.h> |
53 |
#include <stdlib.h> |
54 |
#include <stdio.h> |
55 |
#include <string.h> |
56 |
#include <unistd.h> |
57 |
|
58 |
#include "scan.h" |
59 |
#include <arpa/inet.h> |
60 |
|
61 |
#include <opas/opas.h> |
62 |
#include <peak/peak.h> |
63 |
|
64 |
#include "opas_support.h" |
65 |
#include "cfgloader.h" |
66 |
#include "debug.h" |
67 |
#include "evreg.h" |
68 |
#include "ipcache.h" |
69 |
#include "irc_gline.h" |
70 |
#include "irc_network.h" |
71 |
#include "irc_numnicks.h" |
72 |
#include "irc_send.h" |
73 |
#include "irc_userbase.h" |
74 |
#include "irc_yxx.h" |
75 |
#include "match.h" |
76 |
#include "pxyservd_log.h" |
77 |
|
78 |
#include "PXServiceMsg.h" |
79 |
|
80 |
extern void cmd_status_reply(struct Client *cptr, PXSStatus *status, |
81 |
size_t length); |
82 |
extern void cmd_pxstats_reply(struct Client *cptr, PXSStats *stats); |
83 |
extern void cmd_grem_reply(struct Client *cptr, PXSRemove4 *rem_reply); |
84 |
extern void cmd_recheck_reply(struct Client *cptr, PXSRemove4 *rem_reply); |
85 |
|
86 |
static void scan_timer_callback(peak_timer ti, void *context); |
87 |
static int scan_check_cache(unsigned int flags, void *addrData, |
88 |
time_t *out_scan_ts); |
89 |
static void scan_query(unsigned int flags, const void *addrData, |
90 |
uint32_t user_data); |
91 |
static void scan_client_add(struct Client *cptr); |
92 |
|
93 |
uint32_t l1_cache_hits; /* stats */ |
94 |
|
95 |
/* no-proxy little caches */ |
96 |
static ipcache_t ipcache4; |
97 |
#ifdef ENABLE_IPV6 |
98 |
static ipcache_t ipcache6; |
99 |
#endif |
100 |
static struct Client *scan_client_head, **scan_client_tail_p; |
101 |
static int scan_client_count; |
102 |
static int unscannable_client_count; |
103 |
static peak_timer scan_timer; |
104 |
|
105 |
void |
106 |
scan_init() |
107 |
{ |
108 |
ipcache4 = ipcache_create_in4(IP4_L1_CACHE_SIZE, 0); |
109 |
#ifdef ENABLE_IPV6 |
110 |
ipcache6 = ipcache_create_in6(IP6_L1_CACHE_SIZE, 0); |
111 |
#endif |
112 |
if (opas_support_init() == -1) |
113 |
{ |
114 |
Debug((DL_BASIC, "scan_init: opas_support_init failed!")); |
115 |
/* XXX */ |
116 |
abort(); |
117 |
} |
118 |
|
119 |
scan_client_head = NULL; |
120 |
scan_client_tail_p = NULL; |
121 |
scan_client_count = 0; |
122 |
l1_cache_hits = 0; |
123 |
unscannable_client_count = 0; |
124 |
|
125 |
scan_timer = peak_timer_create(SCAN_TIMER_DELAY, SCAN_TIMER_DELAY, |
126 |
scan_timer_callback, NULL); |
127 |
peak_task_timer_add(peak_task_self(), scan_timer); |
128 |
peak_release(scan_timer); /* so that peak_task_timer_remove() releases it */ |
129 |
} |
130 |
|
131 |
void |
132 |
scan_finalize() |
133 |
{ |
134 |
peak_task_timer_remove(peak_task_self(), scan_timer); /* implicit release */ |
135 |
|
136 |
opas_support_finalize(); |
137 |
|
138 |
#ifdef ENABLE_IPV6 |
139 |
ipcache_dispose(ipcache6); |
140 |
#endif |
141 |
ipcache_dispose(ipcache4); |
142 |
} |
143 |
|
144 |
static void |
145 |
scan_timer_callback(peak_timer ti, void *context) |
146 |
{ |
147 |
struct Client *cptr, *cnext; |
148 |
time_t now = peak_time(); |
149 |
|
150 |
if (!scan_client_count) |
151 |
return; |
152 |
|
153 |
/* Check for timed out scan queries. |
154 |
*/ |
155 |
for (cptr = scan_client_head; cptr; cptr = cnext) |
156 |
{ |
157 |
cnext = cptr->scan_next; |
158 |
|
159 |
/* List is ordered, we know we can break. */ |
160 |
if (cptr->scan_timestamp + SCAN_QUERY_TIMEOUT > now) |
161 |
break; |
162 |
|
163 |
/* Try again ! It's our job damnit. */ |
164 |
scan_client_remove(cptr); |
165 |
scan_start(cptr); |
166 |
} |
167 |
} |
168 |
|
169 |
static int |
170 |
scan_check_cache(unsigned int flags, void *addrData, time_t *out_scan_ts) |
171 |
{ |
172 |
time_t ts = 0; |
173 |
|
174 |
if (!(flags & CLIENT_FLAG_IPV6)) |
175 |
ts = ipcache_find_in4(ipcache4, (struct in_addr *)addrData, NULL); |
176 |
#ifdef ENABLE_IPV6 |
177 |
else |
178 |
ts = ipcache_find_in6(ipcache6, (struct in6_addr *)addrData, NULL); |
179 |
#endif |
180 |
|
181 |
if (ts > 0 && (peak_time() - ts <= IP_L1_CACHE_EXPIRE_DELAY)) |
182 |
{ |
183 |
*out_scan_ts = ts; |
184 |
return 1; |
185 |
} |
186 |
return 0; |
187 |
} |
188 |
|
189 |
int |
190 |
scan_check_noscan_server(struct Server *sptr) |
191 |
{ |
192 |
CNoScanLink *lk; |
193 |
|
194 |
for (lk = gConfig->noscanlist; lk; lk = lk->next) |
195 |
{ |
196 |
if (lk->noscan.type == NOSCAN_TYPE_SERVER |
197 |
&& !match(lk->noscan.u.server, sptr->name)) |
198 |
return 1; /* noscan! */ |
199 |
} |
200 |
return 0; |
201 |
} |
202 |
|
203 |
int |
204 |
scan_check_noscan(const struct Client *cptr) |
205 |
{ |
206 |
CNoScanLink *lk; |
207 |
struct Server *sptr; |
208 |
|
209 |
for (lk = gConfig->noscanlist; lk; lk = lk->next) |
210 |
{ |
211 |
switch (lk->noscan.type) |
212 |
{ |
213 |
case NOSCAN_TYPE_SERVER: |
214 |
sptr = irc_network_get_server(cptr->nserv); |
215 |
if (!match(lk->noscan.u.server, sptr->name)) |
216 |
return 1; /* noscan! */ |
217 |
break; |
218 |
|
219 |
case NOSCAN_TYPE_USERIP: |
220 |
{ |
221 |
if (cptr->flags & CLIENT_FLAG_IPV6) |
222 |
break; /* No IPv6 noscan support yet */ |
223 |
|
224 |
if ((cptr->addr.ip4.s_addr & lk->noscan.u.userip.netmask.s_addr) |
225 |
== (lk->noscan.u.userip.network.s_addr |
226 |
& lk->noscan.u.userip.netmask.s_addr)) |
227 |
return 1; /* noscan! */ |
228 |
break; |
229 |
} |
230 |
} |
231 |
} |
232 |
return 0; |
233 |
} |
234 |
|
235 |
static void |
236 |
scan_query(unsigned int flags, const void *addrData, uint32_t user_data) |
237 |
{ |
238 |
int res; |
239 |
|
240 |
if (!(flags & CLIENT_FLAG_IPV6)) |
241 |
res = opas_support_query((struct in_addr *)addrData, user_data); |
242 |
else |
243 |
res = opas_support_query6((struct in6_addr *)addrData, user_data); |
244 |
|
245 |
if (res == -1) |
246 |
{ |
247 |
char ipbuf[32]; |
248 |
|
249 |
inet_ntop(flags & CLIENT_FLAG_IPV6 ? AF_INET6 : AF_INET, addrData, |
250 |
ipbuf, sizeof(ipbuf)); |
251 |
Debug((DL_BASIC, "scan_query: opas_support_query failed for %s\n", |
252 |
ipbuf)); |
253 |
} |
254 |
} |
255 |
|
256 |
static void |
257 |
scan_client_add(struct Client *cptr) |
258 |
{ |
259 |
assert(!(cptr->flags & CLIENT_FLAG_SCANNING)); |
260 |
|
261 |
/* Set scanning flag for this client. */ |
262 |
cptr->flags |= CLIENT_FLAG_SCANNING; |
263 |
|
264 |
/* Link at tail */ |
265 |
cptr->scan_next = NULL; |
266 |
cptr->scan_prev_p = scan_client_tail_p; |
267 |
|
268 |
if (cptr->scan_prev_p) |
269 |
*cptr->scan_prev_p = cptr; |
270 |
else |
271 |
scan_client_head = cptr; |
272 |
scan_client_tail_p = &cptr->scan_next; |
273 |
|
274 |
scan_client_count++; |
275 |
} |
276 |
|
277 |
void |
278 |
scan_client_remove(struct Client *cptr) |
279 |
{ |
280 |
if (cptr->flags & CLIENT_FLAG_SCANFAIL) |
281 |
unscannable_client_count--; |
282 |
|
283 |
if (!(cptr->flags & CLIENT_FLAG_SCANNING)) |
284 |
return; |
285 |
|
286 |
if (cptr->scan_prev_p) |
287 |
*cptr->scan_prev_p = cptr->scan_next; |
288 |
else |
289 |
scan_client_head = cptr->scan_next; |
290 |
|
291 |
if (cptr->scan_next) |
292 |
cptr->scan_next->scan_prev_p = cptr->scan_prev_p; |
293 |
else |
294 |
scan_client_tail_p = cptr->scan_prev_p; |
295 |
|
296 |
cptr->scan_next = NULL; |
297 |
cptr->scan_prev_p = NULL; |
298 |
|
299 |
scan_client_count--; |
300 |
|
301 |
cptr->flags &= ~CLIENT_FLAG_SCANNING; |
302 |
} |
303 |
|
304 |
int |
305 |
scan_client_get_count() |
306 |
{ |
307 |
return scan_client_count; |
308 |
} |
309 |
|
310 |
int |
311 |
scan_client_get_unscannable_count() |
312 |
{ |
313 |
return unscannable_client_count; |
314 |
} |
315 |
|
316 |
/* External method scan_new_user() |
317 |
* A new user has just connected to the network. |
318 |
*/ |
319 |
void |
320 |
scan_new_user(struct Client *cptr) |
321 |
{ |
322 |
/* First, check the noscan list and ignore user if it matchs |
323 |
*/ |
324 |
if (scan_check_noscan(cptr)) |
325 |
return; |
326 |
|
327 |
/* Then, check our local "level one" cache of scanned IPs. |
328 |
*/ |
329 |
if (scan_check_cache(cptr->flags, &cptr->addr, &cptr->scan_timestamp)) |
330 |
{ |
331 |
l1_cache_hits++; |
332 |
return; |
333 |
} |
334 |
|
335 |
/* Lame notice? ;) |
336 |
*/ |
337 |
if (gConfig->noticelist && |
338 |
!irc_network_server_is_bursting(irc_network_get_server(cptr->nserv))) |
339 |
{ |
340 |
CNoticeLink *notice; |
341 |
|
342 |
for (notice = gConfig->noticelist; notice; notice = notice->next) |
343 |
{ |
344 |
char dst[6]; |
345 |
inttobase64(dst, cptr->nserv, 2); |
346 |
inttobase64(dst + 2, cptr->nnick, 3); |
347 |
dst[5] = '\0'; |
348 |
send_to_one(dst, "%s", notice->line); |
349 |
} |
350 |
} |
351 |
|
352 |
/* No luck, start scan procedure. |
353 |
*/ |
354 |
scan_start(cptr); |
355 |
} |
356 |
|
357 |
void |
358 |
scan_start(struct Client *cptr) |
359 |
{ |
360 |
cptr->scan_timestamp = peak_time(); |
361 |
scan_client_add(cptr); |
362 |
|
363 |
if (opas_support_is_ready()) |
364 |
scan_query(cptr->flags, &cptr->addr, yxx_pack_int(cptr->nserv, |
365 |
cptr->nnick)); |
366 |
} |
367 |
|
368 |
int |
369 |
scan_send_command(struct Client *cptr, uint32_t sig, uint32_t cmd, |
370 |
const void *data, size_t length) |
371 |
{ |
372 |
PXSHeader *hp = (PXSHeader *)data; |
373 |
|
374 |
#if 0 |
375 |
if (cptr->flags & CLIENT_FLAG_COMMAND) |
376 |
return 0; |
377 |
#endif |
378 |
|
379 |
if (!opas_support_is_ready()) |
380 |
return -1; /* failure */ |
381 |
|
382 |
cptr->flags |= CLIENT_FLAG_COMMAND; |
383 |
|
384 |
hp->sig = htonl(sig); |
385 |
hp->ver = htonl(PX_VERSION); |
386 |
hp->cmd = htonl(cmd); |
387 |
|
388 |
opas_support_send_msg_user(hp, length, yxx_pack_int(cptr->nserv, |
389 |
cptr->nnick)); |
390 |
return 0; |
391 |
} |
392 |
|
393 |
int |
394 |
scan_send_simple_command(struct Client *cptr, uint32_t sig, uint32_t cmd) |
395 |
{ |
396 |
PXSHeader head; |
397 |
return scan_send_command(cptr, sig, cmd, &head, sizeof(head)); |
398 |
} |
399 |
|
400 |
/* Replies from opas_support module.. */ |
401 |
void |
402 |
scan_reply_noproxy(const struct in_addr *addrp, uint32_t ud, int cached) |
403 |
{ |
404 |
struct Client *cptr = irc_network_find_client(yxx_unpack(ud)); |
405 |
/* Verify that, if a client still exists for this numeric, he has the same |
406 |
* IP that we've just scanned. |
407 |
*/ |
408 |
if (cptr && cptr->addr.ip4.s_addr == addrp->s_addr |
409 |
&& (cptr->flags & CLIENT_FLAG_SCANNING)) |
410 |
{ |
411 |
scan_client_remove(cptr); |
412 |
ipcache_add_in4(ipcache4, addrp, peak_time(), NULL); |
413 |
Debug((DL_BASIC, "scan_reply_noproxy: scan took %d secs", |
414 |
peak_time() - cptr->scan_timestamp)); |
415 |
} |
416 |
} |
417 |
|
418 |
void |
419 |
scan_reply_proxy(const struct in_addr *addrp, uint32_t ud, int cached, |
420 |
int proxy_type, uint16_t proxy_port, const char *proxy_descr) |
421 |
{ |
422 |
const char *reason; |
423 |
struct Client *cptr = irc_network_find_client(yxx_unpack(ud)); |
424 |
|
425 |
/* Verify that, if a client still exists for this numeric, he has the same |
426 |
* IP that we've just scanned. |
427 |
*/ |
428 |
if (cptr && cptr->addr.ip4.s_addr == addrp->s_addr |
429 |
&& (cptr->flags & CLIENT_FLAG_SCANNING)) |
430 |
{ |
431 |
char ipbuf[16]; |
432 |
int cnt; |
433 |
time_t scantime; |
434 |
|
435 |
scan_client_remove(cptr); |
436 |
|
437 |
scantime = peak_time() - cptr->scan_timestamp; |
438 |
|
439 |
if (!inet_ntop(AF_INET, &cptr->addr.ip4, ipbuf, sizeof(ipbuf))) |
440 |
return; |
441 |
|
442 |
/* /!\ O(n) count but everyone likes it... |
443 |
* Used for proxytop's stats too. |
444 |
*/ |
445 |
cnt = irc_userbase_proxycount(&cptr->addr.ip4); |
446 |
|
447 |
if (cached) |
448 |
{ |
449 |
log_write(LOGID_CURRENT, "*@%s [%ld] %s (%d) cached", ipbuf, cnt, |
450 |
proxy_descr, proxy_port); |
451 |
|
452 |
if (gConfig->client.show_cached) |
453 |
send_msg_client_to_console("PG *@%s [%ld] %s at port %u (cached)", |
454 |
ipbuf, cnt, proxy_descr, proxy_port); |
455 |
|
456 |
evreg_broadcast(EVREG_FLAG_CACHED, |
457 |
"[EV] PG *@%s [%ld] %s at port %u (cached)", |
458 |
ipbuf, cnt, proxy_descr, proxy_port); |
459 |
} |
460 |
else |
461 |
{ |
462 |
/* Logging */ |
463 |
log_write(LOGID_CURRENT, "*@%s [%ld] %s (%d)", ipbuf, cnt, |
464 |
proxy_descr, proxy_port); |
465 |
|
466 |
/* Console channel */ |
467 |
send_msg_client_to_console("PG *@%s [%ld] %s at port %u (%ds)", ipbuf, |
468 |
cnt, proxy_descr, proxy_port, scantime); |
469 |
|
470 |
/* Private event notification */ |
471 |
evreg_broadcast(EVREG_FLAG_NEWPROXY, |
472 |
"[EV] PG *@%s [%ld] %s at port %u (%ds)", |
473 |
ipbuf, cnt, proxy_descr, proxy_port, scantime); |
474 |
} |
475 |
|
476 |
if (proxy_type >= 0 && proxy_type < 8) |
477 |
reason = gConfig->gline.reason[proxy_type]; |
478 |
else |
479 |
reason = gConfig->gline.reason[0]; |
480 |
|
481 |
irc_gline_send(addrp, cnt, reason); |
482 |
} |
483 |
} |
484 |
|
485 |
void |
486 |
scan_reply_error(const struct in_addr *addrp, uint32_t ud, uint32_t error) |
487 |
{ |
488 |
/* Ok, can't scan this IP.. no luck ! */ |
489 |
struct Client *cptr = irc_network_find_client(yxx_unpack(ud)); |
490 |
|
491 |
if (cptr && cptr->addr.ip4.s_addr == addrp->s_addr |
492 |
&& (cptr->flags & CLIENT_FLAG_SCANNING) && error != 1000) |
493 |
{ |
494 |
char ipbuf[16]; |
495 |
|
496 |
scan_client_remove(cptr); |
497 |
cptr->flags |= CLIENT_FLAG_SCANFAIL; |
498 |
unscannable_client_count++; |
499 |
|
500 |
/* Log it. */ |
501 |
if (!inet_ntop(AF_INET, &cptr->addr.ip4, ipbuf, sizeof(ipbuf))) |
502 |
return; |
503 |
|
504 |
log_write(LOGID_UNREACH, "%ld,%s", peak_time(), ipbuf); |
505 |
} |
506 |
} |
507 |
|
508 |
void |
509 |
scan_reply6_noproxy(const struct in6_addr *addrp, uint32_t ud, int cached) |
510 |
{ |
511 |
/* Unused for now (who could we blame ?? :) */ |
512 |
} |
513 |
|
514 |
void |
515 |
scan_reply6_proxy(const struct in6_addr *addrp, uint32_t ud, int cached, |
516 |
int proxy_type, uint16_t proxy_port, const char *proxy_descr) |
517 |
{ |
518 |
/* Unused for now */ |
519 |
} |
520 |
|
521 |
void |
522 |
scan_reply6_error(const struct in6_addr *addrp, uint32_t ud, uint32_t error) |
523 |
{ |
524 |
/* Unused for now */ |
525 |
} |
526 |
|
527 |
void |
528 |
scan_reply_command(void *data, size_t length, uint32_t ud) |
529 |
{ |
530 |
struct Client *cptr = irc_network_find_client(yxx_unpack(ud)); |
531 |
|
532 |
if (cptr && (cptr->flags & CLIENT_FLAG_COMMAND) |
533 |
&& (length > sizeof(PXSHeader))) |
534 |
{ |
535 |
PXSHeader *head = (PXSHeader *)data; |
536 |
|
537 |
head->sig = ntohl(head->sig); |
538 |
head->ver = ntohl(head->ver); |
539 |
head->cmd = ntohl(head->cmd); |
540 |
|
541 |
cptr->flags &= ~CLIENT_FLAG_COMMAND; |
542 |
|
543 |
if (head->sig != PXYSCAND_SIG || head->ver != PX_VERSION) |
544 |
return; |
545 |
|
546 |
/* Reply looks fine */ |
547 |
switch (head->cmd) |
548 |
{ |
549 |
case PX_CMD_STATUS: |
550 |
cmd_status_reply(cptr, (PXSStatus *)data, length); |
551 |
break; |
552 |
case PX_CMD_STATS: |
553 |
cmd_pxstats_reply(cptr, (PXSStats *)data); |
554 |
break; |
555 |
case PX_CMD_REMOVE: |
556 |
if (cptr->flags & CLIENT_FLAG_GREM) |
557 |
cmd_grem_reply(cptr, (PXSRemove4 *)data); |
558 |
else |
559 |
cmd_recheck_reply(cptr, (PXSRemove4 *)data); |
560 |
break; |
561 |
default: |
562 |
break; |
563 |
} |
564 |
} |
565 |
} |