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