]> jfr.im git - irc/evilnet/x3.git/blob - src/mod-sockcheck.c
42ed8b43a7fde554b80fa6c93b71709b1e66600c
[irc/evilnet/x3.git] / src / mod-sockcheck.c
1 /* mod-sockcheck.c - insecure proxy checking
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of x3.
5 *
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
21 #include "conf.h"
22 #include "gline.h"
23 #include "ioset.h"
24 #include "modcmd.h"
25 #include "timeq.h"
26
27 #ifdef HAVE_SYS_SOCKET_H
28 #include <sys/socket.h>
29 #endif
30 #ifdef HAVE_NETINET_IN_H
31 #include <netinet/in.h>
32 #endif
33 #ifdef HAVE_ARPA_INET_H
34 #include <arpa/inet.h>
35 #endif
36
37 /* TODO, 1.3 or later: allow rules like "27374:" "reject:Subseven detected";
38 * (For convenience; right now it assumes that there will be a state
39 * rather than an immediate response upon connection.)
40 */
41
42 #if !defined(SOCKCHECK_DEBUG)
43 #define SOCKCHECK_DEBUG 0
44 #endif
45 #define SOCKCHECK_TEST_DB "sockcheck.conf"
46
47 enum sockcheck_decision {
48 CHECKING,
49 ACCEPT,
50 REJECT
51 };
52
53 typedef struct {
54 enum sockcheck_decision decision;
55 time_t last_touched;
56 const char *reason;
57 struct in_addr addr;
58 char hostname[16];
59 } *sockcheck_cache_info;
60
61 DECLARE_LIST(sci_list, sockcheck_cache_info);
62 DEFINE_LIST(sci_list, sockcheck_cache_info)
63
64 /* Here's the list of hosts that need to be started on.
65 */
66 static struct sci_list pending_sci_list;
67
68 /* Map of previously checked IPs state (if we've accepted the address yet).
69 * The data for each entry is a pointer to a sockcheck_cache_info.
70 */
71 static dict_t checked_ip_dict;
72
73 /* Each sockcheck template is formed as a Mealy state machine (that is,
74 * the output on a state transition is a function of both the current
75 * state and the input). Mealy state machines require fewer states to
76 * match the same input than Moore machines (where the output is only
77 * a function of the current state).
78 *
79 * A state is characterized by sending some data (possibly nothing),
80 * waiting a certain amount of time to receive one of zero or more
81 * responses, and a decision (accept, reject, continue to another
82 * state) based on the received response.
83 */
84
85 struct sockcheck_response {
86 const char *template;
87 struct sockcheck_state *next;
88 };
89
90 DECLARE_LIST(response_list, struct sockcheck_response *);
91 DEFINE_LIST(response_list, struct sockcheck_response *)
92 static unsigned int max_responses;
93
94 struct sockcheck_state {
95 unsigned short port;
96 unsigned short timeout;
97 unsigned short reps;
98 enum sockcheck_decision type;
99 const char *template;
100 struct response_list responses;
101 };
102
103 struct sockcheck_list {
104 unsigned int size, used, refs;
105 struct sockcheck_state **list;
106 };
107
108 /*
109 * List of tests.
110 */
111 static struct sockcheck_list *tests;
112
113 /* Stuff to track client state, one instance per open connection. */
114 struct sockcheck_client {
115 struct io_fd *fd;
116 struct sockcheck_list *tests;
117 sockcheck_cache_info addr;
118 unsigned int client_index;
119 unsigned int test_index;
120 unsigned short test_rep;
121 struct sockcheck_state *state;
122 unsigned int read_size, read_used, read_pos;
123 char *read;
124 const char **resp_state;
125 };
126
127 static struct {
128 unsigned int max_clients;
129 unsigned int max_read;
130 unsigned int gline_duration;
131 unsigned int max_cache_age;
132 struct sockaddr_in *local_addr;
133 int local_addr_len;
134 } sockcheck_conf;
135
136 static unsigned int sockcheck_num_clients;
137 static struct sockcheck_client **client_list;
138 static unsigned int proxies_detected, checked_ip_count;
139 static struct module *sockcheck_module;
140 static struct log_type *PC_LOG;
141 const char *sockcheck_module_deps[] = { NULL };
142
143 static const struct message_entry msgtab[] = {
144 { "PCMSG_PROXY_DEFINITION_FAILED", "Proxy definition failed: %s" },
145 { "PCMSG_PROXY_DEFINITION_SUCCEEDED", "New proxy type defined." },
146 { "PCMSG_UNSCANNABLE_IP", "%s has a spoofed, hidden or localnet IP." },
147 { "PCMSG_ADDRESS_QUEUED", "$b%s$b is now queued to be proxy-checked." },
148 { "PCMSG_ADDRESS_UNRESOLVED", "Unable to resolve $b%s$b to an IP address." },
149 { "PCMSG_CHECKING_ADDRESS", "$b%s$b is currently being checked; unable to clear it." },
150 { "PCMSG_NOT_REMOVED_FROM_CACHE", "$b%s$b was not cached and therefore was not cleared." },
151 { "PCMSG_REMOVED_FROM_CACHE", "$b%s$b was cleared from the cached hosts list." },
152 { "PCMSG_DISABLED", "Proxy scanning is $bdisabled$b." },
153 { "PCMSG_NOT_CACHED", "No proxycheck records exist for IP %s." },
154 { "PCMSG_STATUS_CHECKING", "IP %s proxycheck state: last touched %s ago, still checking" },
155 { "PCMSG_STATUS_ACCEPTED", "IP %s proxycheck state: last touched %s ago, acepted" },
156 { "PCMSG_STATUS_REJECTED", "IP %s proxycheck state: last touched %s ago, rejected: %s" },
157 { "PCMSG_STATUS_UNKNOWN", "IP %s proxycheck state: last touched %s ago, invalid status" },
158 { "PCMSG_STATISTICS", "Since booting, I have checked %d clients for illicit proxies, and detected %d proxy hosts.\nI am currently checking %d clients (out of %d max) and have a backlog of %d more to start on.\nI currently have %d hosts cached.\nI know how to detect %d kinds of proxies." },
159 { NULL, NULL }
160 };
161
162 static struct sockcheck_list *
163 sockcheck_list_alloc(unsigned int size)
164 {
165 struct sockcheck_list *list = malloc(sizeof(*list));
166 list->used = 0;
167 list->refs = 1;
168 list->size = size;
169 list->list = malloc(list->size*sizeof(list->list[0]));
170 return list;
171 }
172
173 static void
174 sockcheck_list_append(struct sockcheck_list *list, struct sockcheck_state *new_item)
175 {
176 if (list->used == list->size) {
177 list->size <<= 1;
178 list->list = realloc(list->list, list->size*sizeof(list->list[0]));
179 }
180 list->list[list->used++] = new_item;
181 }
182
183 static struct sockcheck_list *
184 sockcheck_list_clone(struct sockcheck_list *old_list)
185 {
186 struct sockcheck_list *new_list = malloc(sizeof(*new_list));
187 new_list->used = old_list->used;
188 new_list->refs = 1;
189 new_list->size = old_list->size;
190 new_list->list = malloc(new_list->size*sizeof(new_list->list[0]));
191 memcpy(new_list->list, old_list->list, new_list->used*sizeof(new_list->list[0]));
192 return new_list;
193 }
194
195 static void
196 sockcheck_list_unref(struct sockcheck_list *list)
197 {
198 if (!list || --list->refs > 0) return;
199 free(list->list);
200 free(list);
201 }
202
203 static void
204 sockcheck_issue_gline(sockcheck_cache_info sci)
205 {
206 char *target = alloca(3+strlen(sci->hostname));
207 strcpy(target, "*@");
208 strcpy(target+2, sci->hostname);
209 log_module(PC_LOG, LOG_INFO, "Issuing gline for client at IP %s hostname %s: %s", inet_ntoa(sci->addr), sci->hostname, sci->reason);
210 gline_add("ProxyCheck", target, sockcheck_conf.gline_duration, sci->reason, now, 1, 1);
211 }
212
213 static struct sockcheck_client *
214 sockcheck_alloc_client(sockcheck_cache_info sci)
215 {
216 struct sockcheck_client *client;
217 client = calloc(1, sizeof(*client));
218 client->tests = tests;
219 client->tests->refs++;
220 client->addr = sci;
221 client->read_size = sockcheck_conf.max_read;
222 client->read = malloc(client->read_size);
223 client->resp_state = malloc(max_responses * sizeof(client->resp_state[0]));
224 return client;
225 }
226
227 static void
228 sockcheck_free_client(struct sockcheck_client *client)
229 {
230 if (SOCKCHECK_DEBUG) {
231 log_module(PC_LOG, LOG_INFO, "Goodbye %s (%p)! I set you free!", client->addr->hostname, client);
232 }
233 verify(client);
234 if (client->fd)
235 ioset_close(client->fd->fd, 1);
236 sockcheck_list_unref(client->tests);
237 free(client->read);
238 free(client->resp_state);
239 free(client);
240 }
241
242 static void sockcheck_start_client(unsigned int idx);
243 static void sockcheck_begin_test(struct sockcheck_client *client);
244 static void sockcheck_advance(struct sockcheck_client *client, unsigned int next_state);
245
246 static void
247 sockcheck_timeout_client(void *data)
248 {
249 struct sockcheck_client *client = data;
250 if (SOCKCHECK_DEBUG) {
251 log_module(PC_LOG, LOG_INFO, "Client %s timed out.", client->addr->hostname);
252 }
253 verify(client);
254 sockcheck_advance(client, client->state->responses.used-1);
255 }
256
257 static void
258 sockcheck_print_client(const struct sockcheck_client *client)
259 {
260 static const char *decs[] = {"CHECKING", "ACCEPT", "REJECT"};
261 log_module(PC_LOG, LOG_INFO, "client %p: { addr = %p { decision = %s; last_touched = "FMT_TIME_T"; reason = %s; hostname = \"%s\" }; "
262 "test_index = %d; state = %p { port = %d; type = %s; template = \"%s\"; ... }; "
263 "fd = %p(%d); read = %p; read_size = %d; read_used = %d; read_pos = %d; }",
264 client, client->addr, decs[client->addr->decision], client->addr->last_touched, client->addr->reason, client->addr->hostname,
265 client->test_index, client->state,
266 (client->state ? client->state->port : 0),
267 (client->state ? decs[client->state->type] : "N/A"),
268 (client->state ? client->state->template : "N/A"),
269 client->fd, (client->fd ? client->fd->fd : 0),
270 client->read, client->read_size, client->read_used, client->read_pos);
271 }
272
273 static char hexvals[256] = {
274 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
275 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 */
276 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 */
277 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 */
278 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 */
279 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 */
280 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 */
281 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 112 */
282 };
283
284 static void
285 expand_var(const struct sockcheck_client *client, char var, char **p_expansion, unsigned int *p_exp_length)
286 {
287 extern struct cManagerNode cManager;
288 const char *expansion;
289 unsigned int exp_length;
290 #ifndef __SOLARIS__
291 u_int32_t exp4;
292 u_int16_t exp2;
293 #else
294 uint32_t exp4;
295 uint16_t exp2;
296 #endif
297 /* expand variable */
298 switch (var) {
299 case 'c':
300 expansion = client->addr->hostname;
301 exp_length = strlen(expansion);
302 break;
303 case 'i':
304 exp4 = client->addr->addr.s_addr;
305 exp_length = sizeof(exp4);
306 expansion = (char*)&exp4;
307 break;
308 case 'p':
309 exp2 = htons(client->state->port);
310 exp_length = sizeof(exp2);
311 expansion = (char*)&exp2;
312 break;
313 case 'u':
314 expansion = cManager.uplink->host;
315 exp_length = strlen(expansion);
316 break;
317 default:
318 log_module(PC_LOG, LOG_WARNING, "Request to expand unknown sockcheck variable $%c, using empty expansion.", var);
319 expansion = "";
320 exp_length = 0;
321 }
322 if (p_expansion) {
323 *p_expansion = malloc(exp_length);
324 memcpy(*p_expansion, expansion, exp_length);
325 }
326 if (p_exp_length) {
327 *p_exp_length = exp_length;
328 }
329 }
330
331 static int
332 sockcheck_check_template(const char *template, int is_input)
333 {
334 unsigned int nn;
335 if (is_input && !strcmp(template, "other")) return 1;
336 for (nn=0; template[nn]; nn += 2) {
337 switch (template[nn]) {
338 case '=':
339 if (!template[nn+1]) {
340 log_module(MAIN_LOG, LOG_ERROR, "ProxyCheck template %s had = at end of template; needs a second character.", template);
341 return 0;
342 }
343 break;
344 case '$':
345 switch (template[nn+1]) {
346 case 'c': case 'i': case 'p': case 'u': break;
347 default:
348 log_module(MAIN_LOG, LOG_ERROR, "ProxyCheck template %s refers to unknown variable %c (pos %d).", template, template[nn+1], nn);
349 return 0;
350 }
351 break;
352 case '.':
353 if (!is_input) {
354 log_module(MAIN_LOG, LOG_ERROR, "ProxyCheck template %s: . is only valid in input templates.", template);
355 return 0;
356 }
357 if (template[nn+1] != '.') {
358 log_module(MAIN_LOG, LOG_ERROR, "ProxyCheck template %s expects .. to come in twos (pos %d).", template, nn);
359 return 0;
360 }
361 break;
362 case '0': case '1': case '2': case '3': case '4':
363 case '5': case '6': case '7': case '8': case '9':
364 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
365 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
366 if (!hexvals[(unsigned char)template[nn+1]] && (template[nn+1] != '0')) {
367 log_module(MAIN_LOG, LOG_ERROR, "ProxyCheck template %s expects hex characters to come in twos (pos %d).", template, nn);
368 return 0;
369 }
370 break;
371 default:
372 log_module(MAIN_LOG, LOG_ERROR, "ProxyCheck template %s: unrecognized character '%c' (pos %d).", template, template[nn], nn);
373 return 0;
374 }
375 }
376 return 1;
377 }
378
379 static void
380 sockcheck_elaborate_state(struct sockcheck_client *client)
381 {
382 const char *template;
383 unsigned int nn;
384
385 for (template = client->state->template, nn = 0; template[nn]; nn += 2) {
386 switch (template[nn]) {
387 case '=': ioset_write(client->fd, template+nn+1, 1); break;
388 case '0': case '1': case '2': case '3': case '4':
389 case '5': case '6': case '7': case '8': case '9':
390 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
391 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': {
392 char ch = hexvals[(unsigned char)template[nn]] << 4
393 | hexvals[(unsigned char)template[nn+1]];
394 ioset_write(client->fd, &ch, 1);
395 break;
396 }
397 case '$': {
398 char *expansion;
399 unsigned int exp_length;
400 expand_var(client, template[nn+1], &expansion, &exp_length);
401 ioset_write(client->fd, expansion, exp_length);
402 free(expansion);
403 break;
404 }
405 }
406 }
407 for (nn=0; nn<client->state->responses.used; nn++) {
408 /* Set their resp_state to the start of the response. */
409 client->resp_state[nn] = client->state->responses.list[nn]->template;
410 /* If it doesn't require reading, take it now. */
411 if (client->resp_state[nn] && !*client->resp_state[nn]) {
412 if (SOCKCHECK_DEBUG) {
413 log_module(PC_LOG, LOG_INFO, "Skipping straight to easy option %d for %p.", nn, client);
414 }
415 sockcheck_advance(client, nn);
416 return;
417 }
418 }
419 timeq_add(now + client->state->timeout, sockcheck_timeout_client, client);
420 if (SOCKCHECK_DEBUG) {
421 log_module(PC_LOG, LOG_INFO, "Elaborated state for %s:", client->addr->hostname);
422 sockcheck_print_client(client);
423 }
424 }
425
426 static void
427 sockcheck_decide(struct sockcheck_client *client, enum sockcheck_decision decision)
428 {
429 unsigned int n;
430
431 checked_ip_count++;
432 client->addr->decision = decision;
433 client->addr->last_touched = now;
434 switch (decision) {
435 case ACCEPT:
436 /* do nothing */
437 if (SOCKCHECK_DEBUG) {
438 log_module(PC_LOG, LOG_INFO, "Proxy check passed for client at IP %s hostname %s.", inet_ntoa(client->addr->addr), client->addr->hostname);
439 }
440 break;
441 case REJECT:
442 client->addr->reason = client->state->template;
443 proxies_detected++;
444 sockcheck_issue_gline(client->addr);
445 if (SOCKCHECK_DEBUG) {
446 log_module(PC_LOG, LOG_INFO, "Proxy check rejects client at IP %s hostname %s (%s)", inet_ntoa(client->addr->addr), client->addr->hostname, client->addr->reason);
447 }
448 /* Don't compare test_index != 0 directly, because somebody
449 * else may have reordered the tests already. */
450 if (client->tests->list[client->test_index] != tests->list[0]) {
451 struct sockcheck_list *new_tests = sockcheck_list_clone(tests);
452 struct sockcheck_state *new_first = client->tests->list[client->test_index];
453 for (n=0; (n<tests->used) && (tests->list[n] != new_first); n++) ;
454 for (; n>0; n--) new_tests->list[n] = new_tests->list[n-1];
455 new_tests->list[0] = new_first;
456 sockcheck_list_unref(tests);
457 tests = new_tests;
458 }
459 break;
460 default:
461 log_module(PC_LOG, LOG_ERROR, "BUG: sockcheck_decide(\"%s\", %d): unrecognized decision.", client->addr->hostname, decision);
462 }
463 n = client->client_index;
464 sockcheck_free_client(client);
465 if ((--sockcheck_num_clients < sockcheck_conf.max_clients)
466 && (pending_sci_list.used > 0)) {
467 sockcheck_start_client(n);
468 } else {
469 client_list[n] = 0;
470 }
471 }
472
473 static void
474 sockcheck_advance(struct sockcheck_client *client, unsigned int next_state)
475 {
476 struct sockcheck_state *ns;
477
478 verify(client);
479 timeq_del(0, sockcheck_timeout_client, client, TIMEQ_IGNORE_WHEN);
480 if (SOCKCHECK_DEBUG) {
481 unsigned int n, m;
482 char buffer[201];
483 static const char *hexmap = "0123456789ABCDEF";
484 log_module(PC_LOG, LOG_INFO, "sockcheck_advance(%s) following response %d (type %d) of %d.", client->addr->hostname, next_state, client->state->responses.list[next_state]->next->type, client->state->responses.used);
485 for (n=0; n<client->read_used; n++) {
486 for (m=0; (m<(sizeof(buffer)-1)>>1) && ((n+m) < client->read_used); m++) {
487 buffer[m << 1] = hexmap[client->read[n+m] >> 4];
488 buffer[m << 1 | 1] = hexmap[client->read[n+m] & 15];
489 }
490 buffer[m<<1] = 0;
491 log_module(PC_LOG, LOG_INFO, " .. read data: %s", buffer);
492 n += m;
493 }
494 sockcheck_print_client(client);
495 }
496
497 ns = client->state = client->state->responses.list[next_state]->next;
498 switch (ns->type) {
499 case CHECKING:
500 sockcheck_elaborate_state(client);
501 break;
502 case REJECT:
503 sockcheck_decide(client, REJECT);
504 break;
505 case ACCEPT:
506 if (++client->test_rep < client->tests->list[client->test_index]->reps) {
507 sockcheck_begin_test(client);
508 } else if (++client->test_index < client->tests->used) {
509 client->test_rep = 0;
510 sockcheck_begin_test(client);
511 } else {
512 sockcheck_decide(client, ACCEPT);
513 }
514 break;
515 default:
516 log_module(PC_LOG, LOG_ERROR, "BUG: unknown next-state type %d (after %p).", ns->type, client->state);
517 break;
518 }
519 }
520
521 static void
522 sockcheck_readable(struct io_fd *fd)
523 {
524 /* read what we can from the fd */
525 struct sockcheck_client *client = fd->data;
526 unsigned int nn;
527 int res;
528
529 verify(client);
530 res = read(fd->fd, client->read + client->read_used, client->read_size - client->read_used);
531 if (res < 0) {
532 switch (res = errno) {
533 default:
534 log_module(PC_LOG, LOG_ERROR, "BUG: sockcheck_readable(%d/%s): read() returned errno %d (%s)", fd->fd, client->addr->hostname, errno, strerror(errno));
535 case EAGAIN:
536 return;
537 case ECONNRESET:
538 sockcheck_advance(client, client->state->responses.used - 1);
539 return;
540 }
541 } else if (res == 0) {
542 sockcheck_advance(client, client->state->responses.used - 1);
543 return;
544 } else {
545 client->read_used += res;
546 }
547 if (SOCKCHECK_DEBUG) {
548 unsigned int n, m;
549 char buffer[201];
550 static const char *hexmap = "0123456789ABCDEF";
551 for (n=0; n<client->read_used; n++) {
552 for (m=0; (m<(sizeof(buffer)-1)>>1) && ((n+m) < client->read_used); m++) {
553 buffer[m << 1] = hexmap[client->read[n+m] >> 4];
554 buffer[m << 1 | 1] = hexmap[client->read[n+m] & 15];
555 }
556 buffer[m<<1] = 0;
557 log_module(PC_LOG, LOG_INFO, "read %d bytes data: %s", client->read_used, buffer);
558 n += m;
559 }
560 }
561
562 /* See if what's been read matches any of the expected responses */
563 while (client->read_pos < client->read_used) {
564 unsigned int last_pos = client->read_pos;
565 char bleh;
566 const char *resp_state;
567
568 for (nn=0; nn<(client->state->responses.used-1); nn++) {
569 char *expected;
570 unsigned int exp_length = 1, free_exp = 0;
571 /* compare against possible target */
572 resp_state = client->resp_state[nn];
573 if (resp_state == NULL) continue;
574 switch (*resp_state) {
575 case '=':
576 bleh = resp_state[1];
577 expected = &bleh;
578 break;
579 case '.':
580 /* any character passes */
581 client->read_pos++;
582 exp_length = 0;
583 break;
584 case '0': case '1': case '2': case '3': case '4':
585 case '5': case '6': case '7': case '8': case '9':
586 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
587 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
588 bleh = hexvals[(unsigned char)resp_state[0]] << 4
589 | hexvals[(unsigned char)resp_state[1]];
590 expected = &bleh;
591 break;
592 case '$':
593 expand_var(client, resp_state[1], &expected, &exp_length);
594 free_exp = 1;
595 break;
596 }
597 if (client->read_pos+exp_length <= client->read_used) {
598 if (exp_length && memcmp(client->read+client->read_pos, expected, exp_length)) {
599 resp_state = NULL;
600 } else {
601 client->read_pos += exp_length;
602 }
603 } else {
604 /* can't check the variable yet, so come back later */
605 resp_state -= 2;
606 }
607 if (free_exp) free(expected);
608 if (resp_state) {
609 client->resp_state[nn] = resp_state = resp_state + 2;
610 if (!*resp_state) {
611 sockcheck_advance(client, nn);
612 return;
613 }
614 } else {
615 client->resp_state[nn] = NULL;
616 }
617 }
618 if (last_pos == client->read_pos) break;
619 }
620
621 /* nothing seemed to match. what now? */
622 if (client->read_used >= client->read_size) {
623 /* we got more data than we expected to get .. don't read any more */
624 if (SOCKCHECK_DEBUG) {
625 log_module(PC_LOG, LOG_INFO, "Buffer filled (unmatched) for client %s", client->addr->hostname);
626 }
627 sockcheck_advance(client, client->state->responses.used-1);
628 return;
629 }
630 }
631
632 static void
633 sockcheck_connected(struct io_fd *fd, int rc)
634 {
635 struct sockcheck_client *client = fd->data;
636 verify(client);
637 client->fd = fd;
638 switch (rc) {
639 default:
640 log_module(PC_LOG, LOG_ERROR, "BUG: connect() got error %d (%s) for client at %s.", rc, strerror(rc), client->addr->hostname);
641 case EHOSTUNREACH:
642 case ECONNREFUSED:
643 case ETIMEDOUT:
644 if (SOCKCHECK_DEBUG) {
645 log_module(PC_LOG, LOG_INFO, "Client %s gave us errno %d (%s)", client->addr->hostname, rc, strerror(rc));
646 }
647 sockcheck_advance(client, client->state->responses.used-1);
648 return;
649 case 0: break;
650 }
651 fd->wants_reads = 1;
652 if (SOCKCHECK_DEBUG) {
653 log_module(PC_LOG, LOG_INFO, "Connected: to %s port %d.", client->addr->hostname, client->state->port);
654 }
655 sockcheck_elaborate_state(client);
656 }
657
658 static void
659 sockcheck_begin_test(struct sockcheck_client *client)
660 {
661 struct io_fd *io_fd;
662
663 verify(client);
664 if (client->fd) {
665 ioset_close(client->fd->fd, 1);
666 client->fd = NULL;
667 }
668 do {
669 client->state = client->tests->list[client->test_index];
670 client->read_pos = 0;
671 client->read_used = 0;
672 client->fd = io_fd = ioset_connect((struct sockaddr*)sockcheck_conf.local_addr, sizeof(struct sockaddr), client->addr->hostname, client->state->port, 0, client, sockcheck_connected);
673 if (!io_fd) {
674 client->test_index++;
675 continue;
676 }
677 io_fd->readable_cb = sockcheck_readable;
678 timeq_add(now + client->state->timeout, sockcheck_timeout_client, client);
679 if (SOCKCHECK_DEBUG) {
680 log_module(PC_LOG, LOG_INFO, "Starting proxy check on %s:%d (test %d) with fd %d (%p).", client->addr->hostname, client->state->port, client->test_index, io_fd->fd, io_fd);
681 }
682 return;
683 } while (client->test_index < client->tests->used);
684 /* Ran out of tests to run; accept this client. */
685 sockcheck_decide(client, ACCEPT);
686 }
687
688 static void
689 sockcheck_start_client(unsigned int idx)
690 {
691 sockcheck_cache_info sci;
692 struct sockcheck_client *client;
693
694 if (pending_sci_list.used == 0) return;
695 if (!(sci = pending_sci_list.list[0])) {
696 log_module(PC_LOG, LOG_ERROR, "BUG: sockcheck_start_client(%d) found null pointer in pending_sci_list.", idx);
697 return;
698 }
699 memmove(pending_sci_list.list, pending_sci_list.list+1,
700 (--pending_sci_list.used)*sizeof(pending_sci_list.list[0]));
701 sockcheck_num_clients++;
702 if (!tests) return;
703 client = client_list[idx] = sockcheck_alloc_client(sci);
704 log_module(PC_LOG, LOG_INFO, "Proxy-checking client at %s (%s) as client %d (%p) of %d.", inet_ntoa(sci->addr), sci->hostname, idx, client, sockcheck_num_clients);
705 client->test_rep = 0;
706 client->client_index = idx;
707 sockcheck_begin_test(client);
708 }
709
710 void
711 sockcheck_queue_address(struct in_addr addr)
712 {
713 sockcheck_cache_info sci;
714 char *ipstr=inet_ntoa(addr);
715
716 sci = dict_find(checked_ip_dict, ipstr, NULL);
717 if (sci) {
718 verify(sci);
719 switch (sci->decision) {
720 case CHECKING:
721 /* We are already checking this host. */
722 return;
723 case ACCEPT:
724 if ((sci->last_touched + sockcheck_conf.max_cache_age) >= (unsigned)now) return;
725 break;
726 case REJECT:
727 if ((sci->last_touched + sockcheck_conf.gline_duration) >= (unsigned)now) {
728 sockcheck_issue_gline(sci);
729 return;
730 }
731 break;
732 }
733 dict_remove(checked_ip_dict, sci->hostname);
734 }
735 sci = calloc(1, sizeof(*sci));
736 sci->decision = CHECKING;
737 sci->last_touched = now;
738 sci->reason = NULL;
739 sci->addr = addr;
740 strncpy(sci->hostname, ipstr, sizeof(sci->hostname));
741 dict_insert(checked_ip_dict, sci->hostname, sci);
742 sci_list_append(&pending_sci_list, sci);
743 if (sockcheck_num_clients < sockcheck_conf.max_clients)
744 sockcheck_start_client(sockcheck_num_clients);
745 }
746
747 int
748 sockcheck_uncache_host(const char *name)
749 {
750 sockcheck_cache_info sci;
751 if ((sci = dict_find(checked_ip_dict, name, NULL))
752 && (sci->decision == CHECKING)) {
753 return -1;
754 }
755 return dict_remove(checked_ip_dict, name);
756 }
757
758 static int
759 sockcheck_create_response(const char *key, void *data, void *extra)
760 {
761 const char *str, *end;
762 struct record_data *rd = data;
763 struct sockcheck_state *parent = extra;
764 struct sockcheck_response *resp;
765 dict_t resps;
766 char *templ;
767
768 /* allocate memory and tack it onto parent->responses */
769 resp = malloc(sizeof(*resp));
770 for (end = key; *end != ':' && *end != 0; end += 2 && end) ;
771 templ = malloc(end - key + 1);
772 memcpy(templ, key, end - key);
773 templ[end - key] = 0;
774 resp->template = templ;
775 if (!sockcheck_check_template(resp->template, 1)) _exit(1);
776 resp->next = malloc(sizeof(*resp->next));
777 resp->next->port = parent->port;
778 response_list_append(&parent->responses, resp);
779 /* now figure out how to create resp->next */
780 if ((str = GET_RECORD_QSTRING(rd))) {
781 if (!ircncasecmp(str, "reject", 6)) {
782 resp->next->type = REJECT;
783 } else if (!ircncasecmp(str, "accept", 6)) {
784 resp->next->type = ACCEPT;
785 } else {
786 log_module(PC_LOG, LOG_ERROR, "Error: unknown sockcheck decision `%s', defaulting to accept.", str);
787 resp->next->type = ACCEPT;
788 }
789 if (str[6]) {
790 resp->next->template = strdup(str+7);
791 } else {
792 resp->next->template = strdup("No explanation given");
793 }
794 } else if ((resps = GET_RECORD_OBJECT(rd))) {
795 resp->next->type = CHECKING;
796 response_list_init(&resp->next->responses);
797 if (*end == ':') {
798 resp->next->template = strdup(end+1);
799 if (!sockcheck_check_template(resp->next->template, 0)) _exit(1);
800 } else {
801 resp->next->template = strdup("");
802 }
803 dict_foreach(resps, sockcheck_create_response, resp->next);
804 }
805 return 0;
806 }
807
808 /* key: PORT:send-pattern, as in keys of sockcheck.conf.example
809 * data: recdb record_data containing response
810 * extra: struct sockcheck_list* to append test to
811 */
812 static int
813 sockcheck_create_test(const char *key, void *data, void *extra)
814 {
815 char *end;
816 struct record_data *rd;
817 dict_t object;
818 struct sockcheck_state *new_test;
819 unsigned int n;
820
821 rd = data;
822 new_test = malloc(sizeof(*new_test));
823 new_test->template = NULL;
824 new_test->reps = 1;
825 new_test->port = strtoul(key, &end, 0);
826 new_test->timeout = 5;
827 new_test->type = CHECKING;
828 response_list_init(&new_test->responses);
829 if (!(object = GET_RECORD_OBJECT(rd))) {
830 log_module(PC_LOG, LOG_ERROR, "Error: misformed sockcheck test `%s', skipping it.", key);
831 free(new_test);
832 return 1;
833 }
834 while (*end) {
835 switch (*end) {
836 case '@': new_test->timeout = strtoul(end+1, &end, 0); break;
837 case '*': new_test->reps = strtoul(end+1, &end, 0); break;
838 case ':':
839 new_test->template = strdup(end+1);
840 end += strlen(end);
841 if (!sockcheck_check_template(new_test->template, 0)) _exit(1);
842 break;
843 default:
844 log_module(PC_LOG, LOG_ERROR, "Error: misformed sockcheck test `%s', skipping it.", key);
845 free(new_test);
846 return 1;
847 }
848 }
849 if (!new_test->template) {
850 log_module(PC_LOG, LOG_ERROR, "Error: misformed sockcheck test `%s', skipping it.", key);
851 free(new_test);
852 return 1;
853 }
854 dict_foreach(object, sockcheck_create_response, new_test);
855 /* If none of the responses have template "other", create a
856 * default response that goes to accept. */
857 for (n=0; n<new_test->responses.used; n++) {
858 if (!strcmp(new_test->responses.list[n]->template, "other")) break;
859 }
860 if (n == new_test->responses.used) {
861 rd = alloc_record_data_qstring("accept");
862 sockcheck_create_response("other", rd, new_test);
863 free_record_data(rd);
864 } else if (n != (new_test->responses.used - 1)) {
865 struct sockcheck_response *tmp;
866 /* switch the response for "other" to the end */
867 tmp = new_test->responses.list[new_test->responses.used - 1];
868 new_test->responses.list[new_test->responses.used - 1] = new_test->responses.list[n];
869 new_test->responses.list[n] = tmp;
870 }
871 if (new_test->responses.used > max_responses) {
872 max_responses = new_test->responses.used;
873 }
874 sockcheck_list_append(extra, new_test);
875 return 0;
876 }
877
878 static void
879 sockcheck_read_tests(void)
880 {
881 dict_t test_db;
882 struct sockcheck_list *new_tests;
883 test_db = parse_database(SOCKCHECK_TEST_DB);
884 if (!test_db)
885 return;
886 if (dict_size(test_db) > 0) {
887 new_tests = sockcheck_list_alloc(dict_size(test_db));
888 dict_foreach(test_db, sockcheck_create_test, new_tests);
889 if (tests) sockcheck_list_unref(tests);
890 tests = new_tests;
891 } else {
892 log_module(PC_LOG, LOG_ERROR, "%s was empty - disabling sockcheck.", SOCKCHECK_TEST_DB);
893 }
894 free_database(test_db);
895 }
896
897 void
898 sockcheck_free_state(struct sockcheck_state *state)
899 {
900 unsigned int n;
901 if (state->type == CHECKING) {
902 for (n=0; n<state->responses.used; n++) {
903 free((char*)state->responses.list[n]->template);
904 sockcheck_free_state(state->responses.list[n]->next);
905 free(state->responses.list[n]);
906 }
907 response_list_clean(&state->responses);
908 }
909 free((char*)state->template);
910 free(state);
911 }
912
913 const char *
914 sockcheck_add_test(const char *desc)
915 {
916 struct sockcheck_list *new_tests;
917 const char *reason;
918 char *name;
919 struct record_data *rd;
920
921 if ((reason = parse_record(desc, &name, &rd)))
922 return reason;
923 new_tests = sockcheck_list_clone(tests);
924 if (sockcheck_create_test(name, rd, new_tests)) {
925 sockcheck_list_unref(new_tests);
926 return "Sockcheck test parse error";
927 }
928 sockcheck_list_unref(tests);
929 tests = new_tests;
930 return 0;
931 }
932
933 static void
934 sockcheck_shutdown(void)
935 {
936 unsigned int n;
937
938 if (client_list) {
939 for (n=0; n<sockcheck_conf.max_clients; n++) {
940 if (client_list[n])
941 sockcheck_free_client(client_list[n]);
942 }
943 free(client_list);
944 }
945 sockcheck_num_clients = 0;
946 dict_delete(checked_ip_dict);
947 sci_list_clean(&pending_sci_list);
948 if (tests)
949 for (n=0; n<tests->used; n++)
950 sockcheck_free_state(tests->list[n]);
951 sockcheck_list_unref(tests);
952 if (sockcheck_conf.local_addr) {
953 free(sockcheck_conf.local_addr);
954 sockcheck_conf.local_addr_len = 0;
955 }
956 }
957
958 static void
959 sockcheck_clean_cache(UNUSED_ARG(void *data))
960 {
961 dict_t curr_clients;
962 dict_iterator_t it, next;
963 sockcheck_cache_info sci;
964 unsigned int nn;
965 int max_age;
966
967 if (SOCKCHECK_DEBUG) {
968 struct string_buffer sb;
969 string_buffer_init(&sb);
970 /* Remember which clients we're still checking; we're not allowed to remove them. */
971 for (curr_clients = dict_new(), nn=0; nn < sockcheck_conf.max_clients; nn++) {
972 if (!client_list[nn])
973 continue;
974 dict_insert(curr_clients, client_list[nn]->addr->hostname, client_list[nn]);
975 string_buffer_append(&sb, ' ');
976 string_buffer_append_string(&sb, client_list[nn]->addr->hostname);
977 }
978 string_buffer_append(&sb, '\0');
979 log_module(PC_LOG, LOG_INFO, "Cleaning sockcheck cache at "FMT_TIME_T"; current clients: %s.", now, sb.list);
980 string_buffer_clean(&sb);
981 } else {
982 for (curr_clients = dict_new(), nn=0; nn < sockcheck_conf.max_clients; nn++) {
983 if (!client_list[nn])
984 continue;
985 dict_insert(curr_clients, client_list[nn]->addr->hostname, client_list[nn]);
986 }
987 }
988
989 for (it=dict_first(checked_ip_dict); it; it=next) {
990 next = iter_next(it);
991 sci = iter_data(it);
992 max_age = (sci->decision == REJECT) ? sockcheck_conf.gline_duration : sockcheck_conf.max_cache_age;
993 if (((sci->last_touched + max_age) < now)
994 && !dict_find(curr_clients, sci->hostname, NULL)) {
995 if (SOCKCHECK_DEBUG) {
996 log_module(PC_LOG, LOG_INFO, " .. nuking %s (last touched "FMT_TIME_T").", sci->hostname, sci->last_touched);
997 }
998 dict_remove(checked_ip_dict, sci->hostname);
999 }
1000 }
1001 dict_delete(curr_clients);
1002 timeq_add(now+sockcheck_conf.max_cache_age, sockcheck_clean_cache, 0);
1003 }
1004
1005 static MODCMD_FUNC(cmd_defproxy)
1006 {
1007 const char *reason;
1008
1009 if ((reason = sockcheck_add_test(unsplit_string(argv+1, argc-1, NULL)))) {
1010 reply("PCMSG_PROXY_DEFINITION_FAILED", reason);
1011 return 0;
1012 }
1013 reply("PCMSG_PROXY_DEFINITION_SUCCEEDED");
1014 return 1;
1015 }
1016
1017 static MODCMD_FUNC(cmd_hostscan)
1018 {
1019 unsigned int n;
1020 unsigned long addr;
1021 struct in_addr ipaddr;
1022 char hnamebuf[64];
1023
1024 for (n=1; n<argc; n++) {
1025 struct userNode *un = GetUserH(argv[n]);
1026
1027 if (un) {
1028 if ((un->ip.s_addr == 0) || (ntohl(un->ip.s_addr) == INADDR_LOOPBACK)) {
1029 reply("PCMSG_UNSCANNABLE_IP", un->nick);
1030 } else {
1031 strcpy(hnamebuf, inet_ntoa(un->ip));
1032 sockcheck_queue_address(un->ip);
1033 reply("PCMSG_ADDRESS_QUEUED", hnamebuf);
1034 }
1035 } else {
1036 char *scanhost = argv[n];
1037 if (getipbyname(scanhost, &addr)) {
1038 ipaddr.s_addr = htonl(addr);
1039 sockcheck_queue_address(ipaddr);
1040 reply("PCMSG_ADDRESS_QUEUED", scanhost);
1041 } else {
1042 reply("PCMSG_ADDRESS_UNRESOLVED", scanhost);
1043 }
1044 }
1045 }
1046 return 1;
1047 }
1048
1049 static MODCMD_FUNC(cmd_clearhost)
1050 {
1051 unsigned int n;
1052 char hnamebuf[64];
1053
1054 for (n=1; n<argc; n++) {
1055 struct userNode *un = GetUserH(argv[n]);
1056 const char *scanhost;
1057
1058 if (un) {
1059 strcpy(hnamebuf, inet_ntoa(un->ip));
1060 scanhost = hnamebuf;
1061 } else {
1062 scanhost = argv[n];
1063 }
1064 switch (sockcheck_uncache_host(scanhost)) {
1065 case -1:
1066 reply("PCMSG_CHECKING_ADDRESS", scanhost);
1067 break;
1068 case 0:
1069 reply("PCMSG_NOT_REMOVED_FROM_CACHE", scanhost);
1070 break;
1071 default:
1072 reply("PCMSG_REMOVED_FROM_CACHE", scanhost);
1073 break;
1074 }
1075 }
1076 return 1;
1077 }
1078
1079 static MODCMD_FUNC(cmd_stats_proxycheck)
1080 {
1081 if (argc > 1) {
1082 const char *hostname = argv[1];
1083 char elapse_buf[INTERVALLEN];
1084 const char *msg;
1085
1086 sockcheck_cache_info sci = dict_find(checked_ip_dict, hostname, NULL);
1087 if (!sci) {
1088 reply("PCMSG_NOT_CACHED", hostname);
1089 return 0;
1090 }
1091 intervalString(elapse_buf, now - sci->last_touched, user->handle_info);
1092 switch (sci->decision) {
1093 case CHECKING: msg = "PCMSG_STATUS_CHECKING"; break;
1094 case ACCEPT: msg = "PCMSG_STATUS_ACCEPTED"; break;
1095 case REJECT: msg = "PCMSG_STATUS_REJECTED"; break;
1096 default: msg = "PCMSG_STATUS_UNKNOWN"; break;
1097 }
1098 reply(msg, sci->hostname, elapse_buf, sci->reason);
1099 return 1;
1100 } else {
1101 reply("PCMSG_STATISTICS", checked_ip_count, proxies_detected, sockcheck_num_clients, sockcheck_conf.max_clients, pending_sci_list.used, dict_size(checked_ip_dict), (tests ? tests->used : 0));
1102 return 1;
1103 }
1104 }
1105
1106 static int
1107 sockcheck_new_user(struct userNode *user) {
1108 /* If they have a bum IP, or are bursting in, don't proxy-check or G-line them. */
1109 if (user->ip.s_addr
1110 && (ntohl(user->ip.s_addr) != INADDR_LOOPBACK)
1111 && !user->uplink->burst)
1112 sockcheck_queue_address(user->ip);
1113 return 0;
1114 }
1115
1116 static void
1117 _sockcheck_init(void)
1118 {
1119 checked_ip_dict = dict_new();
1120 dict_set_free_data(checked_ip_dict, free);
1121 sci_list_init(&pending_sci_list);
1122 sockcheck_num_clients = 0;
1123 sockcheck_read_tests();
1124 timeq_del(0, sockcheck_clean_cache, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
1125 client_list = calloc(sockcheck_conf.max_clients, sizeof(client_list[0]));
1126 timeq_add(now+sockcheck_conf.max_cache_age, sockcheck_clean_cache, 0);
1127 }
1128
1129 static void
1130 sockcheck_read_conf(void)
1131 {
1132 dict_t my_node;
1133 const char *str;
1134
1135 /* set the defaults here in case the entire record is missing */
1136 sockcheck_conf.max_clients = 32;
1137 sockcheck_conf.max_read = 1024;
1138 sockcheck_conf.gline_duration = 3600;
1139 sockcheck_conf.max_cache_age = 60;
1140 if (sockcheck_conf.local_addr) {
1141 free(sockcheck_conf.local_addr);
1142 sockcheck_conf.local_addr = NULL;
1143 }
1144 /* now try to read from the conf database */
1145 if ((my_node = conf_get_data("modules/sockcheck", RECDB_OBJECT))) {
1146 str = database_get_data(my_node, "max_sockets", RECDB_QSTRING);
1147 if (str) sockcheck_conf.max_clients = strtoul(str, NULL, 0);
1148 str = database_get_data(my_node, "max_clients", RECDB_QSTRING);
1149 if (str) sockcheck_conf.max_clients = strtoul(str, NULL, 0);
1150 str = database_get_data(my_node, "max_read", RECDB_QSTRING);
1151 if (str) sockcheck_conf.max_read = strtoul(str, NULL, 0);
1152 str = database_get_data(my_node, "max_cache_age", RECDB_QSTRING);
1153 if (str) sockcheck_conf.max_cache_age = ParseInterval(str);
1154 str = database_get_data(my_node, "gline_duration", RECDB_QSTRING);
1155 if (str) sockcheck_conf.gline_duration = ParseInterval(str);
1156 str = database_get_data(my_node, "address", RECDB_QSTRING);
1157 if (str) {
1158 struct sockaddr_in *sin;
1159 unsigned long addr;
1160
1161 sockcheck_conf.local_addr_len = sizeof(*sin);
1162 if (getipbyname(str, &addr)) {
1163 sin = malloc(sockcheck_conf.local_addr_len);
1164 sin->sin_family = AF_INET;
1165 sin->sin_port = 0;
1166 sin->sin_addr.s_addr = addr;
1167 #ifdef HAVE_SIN_LEN
1168 sin->sin_len = 0;
1169 #endif
1170 memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
1171 sockcheck_conf.local_addr = sin;
1172 } else {
1173 log_module(PC_LOG, LOG_ERROR, "Error: Unable to get host named `%s', not checking a specific address.", str);
1174 sockcheck_conf.local_addr = NULL;
1175 }
1176 }
1177 }
1178 }
1179
1180 int
1181 sockcheck_init(void)
1182 {
1183 PC_LOG = log_register_type("ProxyCheck", "file:proxycheck.log");
1184 conf_register_reload(sockcheck_read_conf);
1185 reg_exit_func(sockcheck_shutdown);
1186 _sockcheck_init();
1187 message_register_table(msgtab);
1188
1189 sockcheck_module = module_register("ProxyCheck", PC_LOG, "mod-sockcheck.help", NULL);
1190 modcmd_register(sockcheck_module, "defproxy", cmd_defproxy, 2, 0, "level", "999", NULL);
1191 modcmd_register(sockcheck_module, "hostscan", cmd_hostscan, 2, 0, "level", "650", NULL);
1192 modcmd_register(sockcheck_module, "clearhost", cmd_clearhost, 2, 0, "level", "650", NULL);
1193 modcmd_register(sockcheck_module, "stats proxycheck", cmd_stats_proxycheck, 0, 0, NULL);
1194 reg_new_user_func(sockcheck_new_user);
1195 return 1;
1196 }
1197
1198 int
1199 sockcheck_finalize(void)
1200 {
1201 return 1;
1202 }