License update
[srvx.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 srvx.
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);
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     if (client->fd) ioset_close(client->fd->fd, 1);
234     sockcheck_list_unref(client->tests);
235     free(client->read);
236     free(client->resp_state);
237     free(client);
238 }
239
240 static void sockcheck_start_client(unsigned int idx);
241 static void sockcheck_begin_test(struct sockcheck_client *client);
242 static void sockcheck_advance(struct sockcheck_client *client, unsigned int next_state);
243
244 static void
245 sockcheck_timeout_client(void *data)
246 {
247     struct sockcheck_client *client = data;
248     if (SOCKCHECK_DEBUG) {
249         log_module(PC_LOG, LOG_INFO, "Client %s timed out.", client->addr->hostname);
250     }
251     sockcheck_advance(client, client->state->responses.used-1);
252 }
253
254 static void
255 sockcheck_print_client(const struct sockcheck_client *client)
256 {
257     static const char *decs[] = {"CHECKING", "ACCEPT", "REJECT"};
258     log_module(PC_LOG, LOG_INFO, "client %p: { addr = %p { decision = %s; last_touched = "FMT_TIME_T"; reason = %s; hostname = \"%s\" }; "
259         "test_index = %d; state = %p { port = %d; type = %s; template = \"%s\"; ... }; "
260         "fd = %p(%d); read = %p; read_size = %d; read_used = %d; read_pos = %d; }",
261         client, client->addr, decs[client->addr->decision], client->addr->last_touched, client->addr->reason, client->addr->hostname,
262         client->test_index, client->state,
263         (client->state ? client->state->port : 0),
264         (client->state ? decs[client->state->type] : "N/A"),
265         (client->state ? client->state->template : "N/A"),
266         client->fd, (client->fd ? client->fd->fd : 0),
267         client->read, client->read_size, client->read_used, client->read_pos);
268 }
269
270 static char hexvals[256] = {
271     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
272     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16 */
273     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32 */
274     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 48 */
275     0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 64 */
276     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 */
277     0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 96 */
278     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  /* 112 */
279 };
280
281 static void
282 expand_var(const struct sockcheck_client *client, char var, char **p_expansion, unsigned int *p_exp_length)
283 {
284     extern struct cManagerNode cManager;
285     const char *expansion;
286     unsigned int exp_length;
287 #ifndef __SOLARIS__
288     u_int32_t exp4;
289     u_int16_t exp2;
290 #else
291     uint32_t exp4;
292     uint16_t exp2;
293 #endif
294     /* expand variable */
295     switch (var) {
296     case 'c':
297         expansion = client->addr->hostname;
298         exp_length = strlen(expansion);
299         break;
300     case 'i':
301         exp4 = client->addr->addr.s_addr;
302         exp_length = sizeof(exp4);
303         expansion = (char*)&exp4;
304         break;
305     case 'p':
306         exp2 = htons(client->state->port);
307         exp_length = sizeof(exp2);
308         expansion = (char*)&exp2;
309         break;
310     case 'u':
311         expansion = cManager.uplink->host;
312         exp_length = strlen(expansion);
313         break;
314     default:
315         log_module(PC_LOG, LOG_WARNING, "Request to expand unknown sockcheck variable $%c, using empty expansion.", var);
316         expansion = "";
317         exp_length = 0;
318     }
319     if (p_expansion) {
320         *p_expansion = malloc(exp_length);
321         memcpy(*p_expansion, expansion, exp_length);
322     }
323     if (p_exp_length) {
324         *p_exp_length = exp_length;
325     }
326 }
327
328 static int
329 sockcheck_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
376 static void
377 sockcheck_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. */
406         client->resp_state[nn] = client->state->responses.list[nn]->template;
407         /* If it doesn't require reading, take it now. */
408         if (client->resp_state[nn] && !*client->resp_state[nn]) {
409             if (SOCKCHECK_DEBUG) {
410                 log_module(PC_LOG, LOG_INFO, "Skipping straight to easy option %d for %p.", nn, client);
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
423 static void
424 sockcheck_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:
433         /* do nothing */
434         if (SOCKCHECK_DEBUG) {
435             log_module(PC_LOG, LOG_INFO, "Proxy check passed for client at IP %s hostname %s.", inet_ntoa(client->addr->addr), client->addr->hostname);
436         }
437         break;
438     case REJECT:
439         client->addr->reason = client->state->template;
440         proxies_detected++;
441         sockcheck_issue_gline(client->addr);
442         if (SOCKCHECK_DEBUG) {
443             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);
444         }
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];
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];
452             new_tests->list[0] = new_first;
453             sockcheck_list_unref(tests);
454             tests = new_tests;
455         }
456         break;
457     default:
458         log_module(PC_LOG, LOG_ERROR, "BUG: sockcheck_decide(\"%s\", %d): unrecognized decision.", client->addr->hostname, decision);
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
470 static void
471 sockcheck_advance(struct sockcheck_client *client, unsigned int next_state)
472 {
473     struct sockcheck_state *ns;
474
475     timeq_del(0, sockcheck_timeout_client, client, TIMEQ_IGNORE_WHEN);
476     if (SOCKCHECK_DEBUG) {
477         unsigned int n, m;
478         char buffer[201];
479         static const char *hexmap = "0123456789ABCDEF";
480         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);
481         for (n=0; n<client->read_used; n++) {
482             for (m=0; (m<(sizeof(buffer)-1)>>1) && ((n+m) < client->read_used); m++) {
483                 buffer[m << 1] = hexmap[client->read[n+m] >> 4];
484                 buffer[m << 1 | 1] = hexmap[client->read[n+m] & 15];
485             }
486             buffer[m<<1] = 0;
487             log_module(PC_LOG, LOG_INFO, " .. read data: %s", buffer);
488             n += m;
489         }
490         sockcheck_print_client(client);
491     }
492
493     ns = client->state = client->state->responses.list[next_state]->next;
494     switch (ns->type) {
495     case CHECKING:
496         sockcheck_elaborate_state(client);
497         break;
498     case REJECT:
499         sockcheck_decide(client, REJECT);
500         break;
501     case ACCEPT:
502         if (++client->test_rep < client->tests->list[client->test_index]->reps) {
503             sockcheck_begin_test(client);
504         } else if (++client->test_index < client->tests->used) {
505             client->test_rep = 0;
506             sockcheck_begin_test(client);
507         } else {
508             sockcheck_decide(client, ACCEPT);
509         }
510         break;
511     default:
512         log_module(PC_LOG, LOG_ERROR, "BUG: unknown next-state type %d (after %p).", ns->type, client->state);
513         break;
514     }
515 }
516
517 static void
518 sockcheck_readable(struct io_fd *fd)
519 {
520     /* read what we can from the fd */
521     struct sockcheck_client *client = fd->data;
522     unsigned int nn;
523     int res;
524
525     res = read(fd->fd, client->read + client->read_used, client->read_size - client->read_used);
526     if (res < 0) {
527         switch (res = errno) {
528         default:
529             log_module(PC_LOG, LOG_ERROR, "BUG: sockcheck_readable(%d/%s): read() returned errno %d (%s)", fd->fd, client->addr->hostname, errno, strerror(errno));
530         case EAGAIN:
531             return;
532         case ECONNRESET:
533             sockcheck_advance(client, client->state->responses.used - 1);
534             return;
535         }
536     } else if (res == 0) {
537         sockcheck_advance(client, client->state->responses.used - 1);
538         return;
539     } else {
540         client->read_used += res;
541     }
542     if (SOCKCHECK_DEBUG) {
543         unsigned int n, m;
544         char buffer[201];
545         static const char *hexmap = "0123456789ABCDEF";
546         for (n=0; n<client->read_used; n++) {
547             for (m=0; (m<(sizeof(buffer)-1)>>1) && ((n+m) < client->read_used); m++) {
548                 buffer[m << 1] = hexmap[client->read[n+m] >> 4];
549                 buffer[m << 1 | 1] = hexmap[client->read[n+m] & 15];
550             }
551             buffer[m<<1] = 0;
552             log_module(PC_LOG, LOG_INFO, "read %d bytes data: %s", client->read_used, buffer);
553             n += m;
554         }
555     }
556
557     /* See if what's been read matches any of the expected responses */
558     while (client->read_pos < client->read_used) {
559         unsigned int last_pos = client->read_pos;
560         char bleh;
561         const char *resp_state;
562
563         for (nn=0; nn<(client->state->responses.used-1); nn++) {
564             char *expected;
565             unsigned int exp_length = 1, free_exp = 0;
566             /* compare against possible target */
567             resp_state = client->resp_state[nn];
568             if (resp_state == NULL) continue;
569             switch (*resp_state) {
570             case '=': 
571                 bleh = resp_state[1];
572                 expected = &bleh;
573                 break;
574             case '.':
575                 /* any character passes */
576                 client->read_pos++;
577                 exp_length = 0;
578                 break;
579             case '0': case '1': case '2': case '3': case '4':
580             case '5': case '6': case '7': case '8': case '9':
581             case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
582             case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
583                 bleh = hexvals[(unsigned char)resp_state[0]] << 4
584                     | hexvals[(unsigned char)resp_state[1]];
585                 expected = &bleh;
586                 break;
587             case '$':
588                 expand_var(client, resp_state[1], &expected, &exp_length);
589                 free_exp = 1;
590                 break;
591             }
592             if (client->read_pos+exp_length <= client->read_used) {
593                 if (exp_length && memcmp(client->read+client->read_pos, expected, exp_length)) {
594                     resp_state = NULL;
595                 } else {
596                     client->read_pos += exp_length;
597                 }
598             } else {
599                 /* can't check the variable yet, so come back later */
600                 resp_state -= 2;
601             }
602             if (free_exp) free(expected);
603             if (resp_state) {
604                 client->resp_state[nn] = resp_state = resp_state + 2;
605                 if (!*resp_state) {
606                     sockcheck_advance(client, nn);
607                     return;
608                 }
609             } else {
610                 client->resp_state[nn] = NULL;
611             }
612         }
613         if (last_pos == client->read_pos) break;
614     }
615
616     /* nothing seemed to match.  what now? */
617     if (client->read_used >= client->read_size) {
618         /* we got more data than we expected to get .. don't read any more */
619         if (SOCKCHECK_DEBUG) {
620             log_module(PC_LOG, LOG_INFO, "Buffer filled (unmatched) for client %s", client->addr->hostname);
621         }
622         sockcheck_advance(client, client->state->responses.used-1);
623         return;
624     }
625 }
626
627 static void
628 sockcheck_connected(struct io_fd *fd, int rc)
629 {
630     struct sockcheck_client *client = fd->data;
631     client->fd = fd;
632     switch (rc) {
633     default:
634         log_module(PC_LOG, LOG_ERROR, "BUG: connect() got error %d (%s) for client at %s.", rc, strerror(rc), client->addr->hostname);
635     case EHOSTUNREACH:
636     case ECONNREFUSED:
637     case ETIMEDOUT:
638         if (SOCKCHECK_DEBUG) {
639             log_module(PC_LOG, LOG_INFO, "Client %s gave us errno %d (%s)", client->addr->hostname, rc, strerror(rc));
640         }
641         sockcheck_advance(client, client->state->responses.used-1);
642         return;
643     case 0: break;
644     }
645     fd->wants_reads = 1;
646     if (SOCKCHECK_DEBUG) {
647         log_module(PC_LOG, LOG_INFO, "Connected: to %s port %d.", client->addr->hostname, client->state->port);
648     }
649     sockcheck_elaborate_state(client);
650 }
651
652 static void
653 sockcheck_begin_test(struct sockcheck_client *client)
654 {
655     struct io_fd *io_fd;
656
657     if (client->fd) {
658         ioset_close(client->fd->fd, 1);
659         client->fd = NULL;
660     }
661     do {
662         client->state = client->tests->list[client->test_index];
663         client->read_pos = 0;
664         client->read_used = 0;
665         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);
666         if (!io_fd) {
667             client->test_index++;
668             continue;
669         }
670         io_fd->readable_cb = sockcheck_readable;
671         timeq_add(now + client->state->timeout, sockcheck_timeout_client, client);
672         if (SOCKCHECK_DEBUG) {
673             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);
674         }
675         return;
676     } while (client->test_index < client->tests->used);
677     /* Ran out of tests to run; accept this client. */
678     sockcheck_decide(client, ACCEPT);
679 }
680
681 static void
682 sockcheck_start_client(unsigned int idx)
683 {
684     sockcheck_cache_info sci;
685     struct sockcheck_client *client;
686
687     if (pending_sci_list.used == 0) return;
688     if (!(sci = pending_sci_list.list[0])) {
689         log_module(PC_LOG, LOG_ERROR, "BUG: sockcheck_start_client(%d) found null pointer in pending_sci_list.", idx);
690         return;
691     }
692     memmove(pending_sci_list.list, pending_sci_list.list+1,
693             (--pending_sci_list.used)*sizeof(pending_sci_list.list[0]));
694     sockcheck_num_clients++;
695     if (!tests) return;
696     client = client_list[idx] = sockcheck_alloc_client(sci);
697     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);
698     client->test_rep = 0;
699     client->client_index = idx;
700     sockcheck_begin_test(client);
701 }
702
703 void
704 sockcheck_queue_address(struct in_addr addr)
705 {
706     sockcheck_cache_info sci;
707     char *ipstr=inet_ntoa(addr);
708
709     sci = dict_find(checked_ip_dict, ipstr, NULL);
710     if (sci) {
711         switch (sci->decision) {
712         case CHECKING:
713             /* We are already checking this host. */
714             return;
715         case ACCEPT:
716             if ((sci->last_touched + sockcheck_conf.max_cache_age) >= (unsigned)now) return;
717             break;
718         case REJECT:
719             if ((sci->last_touched + sockcheck_conf.gline_duration) >= (unsigned)now) {
720                 sockcheck_issue_gline(sci);
721                 return;
722             }
723             break;
724         }
725         dict_remove(checked_ip_dict, sci->hostname);
726     }
727     sci = calloc(1, sizeof(*sci));
728     sci->decision = CHECKING;
729     sci->last_touched = now;
730     sci->reason = NULL;
731     sci->addr = addr;
732     strncpy(sci->hostname, ipstr, sizeof(sci->hostname));
733     dict_insert(checked_ip_dict, sci->hostname, sci);
734     sci_list_append(&pending_sci_list, sci);
735     if (sockcheck_num_clients < sockcheck_conf.max_clients)
736         sockcheck_start_client(sockcheck_num_clients);
737 }
738
739 int
740 sockcheck_uncache_host(const char *name)
741 {
742     sockcheck_cache_info sci;
743     if ((sci = dict_find(checked_ip_dict, name, NULL))
744         && (sci->decision == CHECKING)) {
745         return -1;
746     }
747     return dict_remove(checked_ip_dict, name);
748 }
749
750 static int
751 sockcheck_create_response(const char *key, void *data, void *extra)
752 {
753     const char *str, *end;
754     struct record_data *rd = data;
755     struct sockcheck_state *parent = extra;
756     struct sockcheck_response *resp;
757     dict_t resps;
758     char *templ;
759
760     /* allocate memory and tack it onto parent->responses */
761     resp = malloc(sizeof(*resp));
762     for (end = key; *end != ':' && *end != 0; end += 2 && end) ;
763     templ = malloc(end - key + 1);
764     memcpy(templ, key, end - key);
765     templ[end - key] = 0;
766     resp->template = templ;
767     if (!sockcheck_check_template(resp->template, 1)) _exit(1);
768     resp->next = malloc(sizeof(*resp->next));
769     resp->next->port = parent->port;
770     response_list_append(&parent->responses, resp);
771     /* now figure out how to create resp->next */
772     if ((str = GET_RECORD_QSTRING(rd))) {
773         if (!ircncasecmp(str, "reject", 6)) {
774             resp->next->type = REJECT;
775         } else if (!ircncasecmp(str, "accept", 6)) {
776             resp->next->type = ACCEPT;
777         } else {
778             log_module(PC_LOG, LOG_ERROR, "Error: unknown sockcheck decision `%s', defaulting to accept.", str);
779             resp->next->type = ACCEPT;
780         }
781         if (str[6]) {
782             resp->next->template = strdup(str+7);
783         } else {
784             resp->next->template = strdup("No explanation given");
785         }
786     } else if ((resps = GET_RECORD_OBJECT(rd))) {
787         resp->next->type = CHECKING;
788         response_list_init(&resp->next->responses);
789         if (*end == ':') {
790             resp->next->template = strdup(end+1);
791             if (!sockcheck_check_template(resp->next->template, 0)) _exit(1);
792         } else {
793             resp->next->template = strdup("");
794         }
795         dict_foreach(resps, sockcheck_create_response, resp->next);
796     }
797     return 0;
798 }
799
800 /* key: PORT:send-pattern, as in keys of sockcheck.conf.example
801  * data: recdb record_data containing response
802  * extra: struct sockcheck_list* to append test to
803  */
804 static int
805 sockcheck_create_test(const char *key, void *data, void *extra)
806 {
807     char *end;
808     struct record_data *rd;
809     dict_t object;
810     struct sockcheck_state *new_test;
811     unsigned int n;
812
813     rd = data;
814     new_test = malloc(sizeof(*new_test));
815     new_test->template = NULL;
816     new_test->reps = 1;
817     new_test->port = strtoul(key, &end, 0);
818     new_test->timeout = 5;
819     new_test->type = CHECKING;
820     response_list_init(&new_test->responses);
821     if (!(object = GET_RECORD_OBJECT(rd))) {
822         log_module(PC_LOG, LOG_ERROR, "Error: misformed sockcheck test `%s', skipping it.", key);
823         free(new_test);
824         return 1;
825     }
826     while (*end) {
827         switch (*end) {
828         case '@': new_test->timeout = strtoul(end+1, &end, 0); break;
829         case '*': new_test->reps = strtoul(end+1, &end, 0); break;
830         case ':':
831             new_test->template = strdup(end+1);
832             end += strlen(end);
833             if (!sockcheck_check_template(new_test->template, 0)) _exit(1);
834             break;
835         default:
836             log_module(PC_LOG, LOG_ERROR, "Error: misformed sockcheck test `%s', skipping it.", key);
837             free(new_test);
838             return 1;
839         }
840     }
841     if (!new_test->template) {
842         log_module(PC_LOG, LOG_ERROR, "Error: misformed sockcheck test `%s', skipping it.", key);
843         free(new_test);
844         return 1;
845     }
846     dict_foreach(object, sockcheck_create_response, new_test);
847     /* If none of the responses have template "other", create a
848      * default response that goes to accept. */
849     for (n=0; n<new_test->responses.used; n++) {
850         if (!strcmp(new_test->responses.list[n]->template, "other")) break;
851     }
852     if (n == new_test->responses.used) {
853         rd = alloc_record_data_qstring("accept");
854         sockcheck_create_response("other", rd, new_test);
855         free_record_data(rd);
856     } else if (n != (new_test->responses.used - 1)) {
857         struct sockcheck_response *tmp;
858         /* switch the response for "other" to the end */
859         tmp = new_test->responses.list[new_test->responses.used - 1];
860         new_test->responses.list[new_test->responses.used - 1] = new_test->responses.list[n];
861         new_test->responses.list[n] = tmp;
862     }
863     if (new_test->responses.used > max_responses) {
864         max_responses = new_test->responses.used;
865     }
866     sockcheck_list_append(extra, new_test);
867     return 0;
868 }
869
870 static void
871 sockcheck_read_tests(void)
872 {
873     dict_t test_db;
874     struct sockcheck_list *new_tests;
875     test_db = parse_database(SOCKCHECK_TEST_DB);
876     if (!test_db)
877         return;
878     if (dict_size(test_db) > 0) {
879         new_tests = sockcheck_list_alloc(dict_size(test_db));
880         dict_foreach(test_db, sockcheck_create_test, new_tests);
881         if (tests) sockcheck_list_unref(tests);
882         tests = new_tests;
883     } else {
884         log_module(PC_LOG, LOG_ERROR, "%s was empty - disabling sockcheck.", SOCKCHECK_TEST_DB);
885     }
886     free_database(test_db);
887 }
888
889 void
890 sockcheck_free_state(struct sockcheck_state *state)
891 {
892     unsigned int n;
893     if (state->type == CHECKING) {
894         for (n=0; n<state->responses.used; n++) {
895             free((char*)state->responses.list[n]->template);
896             sockcheck_free_state(state->responses.list[n]->next);
897             free(state->responses.list[n]);
898         }
899         response_list_clean(&state->responses);
900     }
901     free((char*)state->template);
902     free(state);
903 }
904
905 const char *
906 sockcheck_add_test(const char *desc)
907 {
908     struct sockcheck_list *new_tests;
909     const char *reason;
910     char *name;
911     struct record_data *rd;
912
913     if ((reason = parse_record(desc, &name, &rd)))
914         return reason;
915     new_tests = sockcheck_list_clone(tests);
916     if (sockcheck_create_test(name, rd, new_tests)) {
917         sockcheck_list_unref(new_tests);
918         return "Sockcheck test parse error";
919     }
920     sockcheck_list_unref(tests);
921     tests = new_tests;
922     return 0;
923 }
924
925 static void
926 sockcheck_shutdown(void)
927 {
928     unsigned int n;
929
930     if (client_list) {
931         for (n=0; n<sockcheck_conf.max_clients; n++) {
932             if (client_list[n])
933                 sockcheck_free_client(client_list[n]);
934         }
935         free(client_list);
936     }
937     sockcheck_num_clients = 0;
938     dict_delete(checked_ip_dict);
939     sci_list_clean(&pending_sci_list);
940     if (tests)
941         for (n=0; n<tests->used; n++)
942             sockcheck_free_state(tests->list[n]);
943     sockcheck_list_unref(tests);
944     if (sockcheck_conf.local_addr) {
945         free(sockcheck_conf.local_addr);
946         sockcheck_conf.local_addr_len = 0;
947     }
948 }
949
950 static void
951 sockcheck_clean_cache(UNUSED_ARG(void *data))
952 {
953     dict_t curr_clients;
954     dict_iterator_t it, next;
955     sockcheck_cache_info sci;
956     unsigned int nn;
957     int max_age;
958
959     if (SOCKCHECK_DEBUG) {
960         struct string_buffer sb;
961         string_buffer_init(&sb);
962         /* Remember which clients we're still checking; we're not allowed to remove them. */
963         for (curr_clients = dict_new(), nn=0; nn < sockcheck_conf.max_clients; nn++) {
964             if (!client_list[nn])
965                 continue;
966             dict_insert(curr_clients, client_list[nn]->addr->hostname, client_list[nn]);
967             string_buffer_append(&sb, ' ');
968             string_buffer_append_string(&sb, client_list[nn]->addr->hostname);
969         }
970         string_buffer_append(&sb, '\0');
971         log_module(PC_LOG, LOG_INFO, "Cleaning sockcheck cache at "FMT_TIME_T"; current clients: %s.", now, sb.list);
972         string_buffer_clean(&sb);
973     } else {
974         for (curr_clients = dict_new(), nn=0; nn < sockcheck_conf.max_clients; nn++) {
975             if (!client_list[nn])
976                 continue;
977             dict_insert(curr_clients, client_list[nn]->addr->hostname, client_list[nn]);
978         }
979     }
980
981     for (it=dict_first(checked_ip_dict); it; it=next) {
982         next = iter_next(it);
983         sci = iter_data(it);
984         max_age = (sci->decision == REJECT) ? sockcheck_conf.gline_duration : sockcheck_conf.max_cache_age;
985         if (((sci->last_touched + max_age) < now)
986             && !dict_find(curr_clients, sci->hostname, NULL)) {
987             if (SOCKCHECK_DEBUG) {
988                 log_module(PC_LOG, LOG_INFO, " .. nuking %s (last touched "FMT_TIME_T").", sci->hostname, sci->last_touched);
989             }
990             dict_remove(checked_ip_dict, sci->hostname);
991         }
992     }
993     dict_delete(curr_clients);
994     timeq_add(now+sockcheck_conf.max_cache_age, sockcheck_clean_cache, 0);
995 }
996
997 static MODCMD_FUNC(cmd_defproxy)
998 {
999     const char *reason;
1000
1001     if ((reason = sockcheck_add_test(unsplit_string(argv+1, argc-1, NULL)))) {
1002         reply("PCMSG_PROXY_DEFINITION_FAILED", reason);
1003         return 0;
1004     }
1005     reply("PCMSG_PROXY_DEFINITION_SUCCEEDED");
1006     return 1;
1007 }
1008
1009 static MODCMD_FUNC(cmd_hostscan)
1010 {
1011     unsigned int n;
1012     unsigned long addr;
1013     struct in_addr ipaddr;
1014     char hnamebuf[64];
1015
1016     for (n=1; n<argc; n++) {
1017         struct userNode *un = GetUserH(argv[n]);
1018
1019         if (un) {
1020             if ((un->ip.s_addr == 0) || (ntohl(un->ip.s_addr) == INADDR_LOOPBACK)) {
1021                 reply("PCMSG_UNSCANNABLE_IP", un->nick);
1022             } else {
1023                 strcpy(hnamebuf, inet_ntoa(un->ip));
1024                 sockcheck_queue_address(un->ip);
1025                 reply("PCMSG_ADDRESS_QUEUED", hnamebuf);
1026             }
1027         } else {
1028             char *scanhost = argv[n];
1029             if (getipbyname(scanhost, &addr)) {
1030                 ipaddr.s_addr = htonl(addr);
1031                 sockcheck_queue_address(ipaddr);
1032                 reply("PCMSG_ADDRESS_QUEUED", scanhost);
1033             } else {
1034                 reply("PCMSG_ADDRESS_UNRESOLVED", scanhost);
1035             }
1036         }
1037     }
1038     return 1;
1039 }
1040
1041 static MODCMD_FUNC(cmd_clearhost)
1042 {
1043     unsigned int n;
1044     char hnamebuf[64];
1045
1046     for (n=1; n<argc; n++) {
1047         struct userNode *un = GetUserH(argv[n]);
1048         const char *scanhost;
1049
1050         if (un) {
1051             strcpy(hnamebuf, inet_ntoa(un->ip));
1052             scanhost = hnamebuf;
1053         } else {
1054             scanhost = argv[n];
1055         }
1056         switch (sockcheck_uncache_host(scanhost)) {
1057         case -1:
1058             reply("PCMSG_CHECKING_ADDRESS", scanhost);
1059             break;
1060         case 0:
1061             reply("PCMSG_NOT_REMOVED_FROM_CACHE", scanhost);
1062             break;
1063         default:
1064             reply("PCMSG_REMOVED_FROM_CACHE", scanhost);
1065             break;
1066         }
1067     }
1068     return 1;
1069 }
1070
1071 static MODCMD_FUNC(cmd_stats_proxycheck)
1072 {
1073     if (argc > 1) {
1074         const char *hostname = argv[1];
1075         char elapse_buf[INTERVALLEN];
1076         const char *msg;
1077
1078         sockcheck_cache_info sci = dict_find(checked_ip_dict, hostname, NULL);
1079         if (!sci) {
1080             reply("PCMSG_NOT_CACHED", hostname);
1081             return 0;
1082         }
1083         intervalString(elapse_buf, now - sci->last_touched);
1084         switch (sci->decision) {
1085         case CHECKING: msg = "PCMSG_STATUS_CHECKING"; break;
1086         case ACCEPT: msg = "PCMSG_STATUS_ACCEPTED"; break;
1087         case REJECT: msg = "PCMSG_STATUS_REJECTED"; break;
1088         default: msg = "PCMSG_STATUS_UNKNOWN"; break;
1089         }
1090         reply(msg, sci->hostname, elapse_buf, sci->reason);
1091         return 1;
1092     } else {
1093         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));
1094         return 1;
1095     }
1096 }
1097
1098 static int
1099 sockcheck_new_user(struct userNode *user) {
1100     /* If they have a bum IP, or are bursting in, don't proxy-check or G-line them. */
1101     if (user->ip.s_addr
1102         && (ntohl(user->ip.s_addr) != INADDR_LOOPBACK)
1103         && !user->uplink->burst)
1104         sockcheck_queue_address(user->ip);
1105     return 0;
1106 }
1107
1108 static void
1109 _sockcheck_init(void)
1110 {
1111     checked_ip_dict = dict_new();
1112     dict_set_free_data(checked_ip_dict, free);
1113     sci_list_init(&pending_sci_list);
1114     sockcheck_num_clients = 0;
1115     sockcheck_read_tests();
1116     timeq_del(0, sockcheck_clean_cache, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
1117     client_list = calloc(sockcheck_conf.max_clients, sizeof(client_list[0]));
1118     timeq_add(now+sockcheck_conf.max_cache_age, sockcheck_clean_cache, 0);
1119 }
1120
1121 static void
1122 sockcheck_read_conf(void)
1123 {
1124     dict_t my_node;
1125     const char *str;
1126
1127     /* set the defaults here in case the entire record is missing */
1128     sockcheck_conf.max_clients = 32;
1129     sockcheck_conf.max_read = 1024;
1130     sockcheck_conf.gline_duration = 3600;
1131     sockcheck_conf.max_cache_age = 60;
1132     if (sockcheck_conf.local_addr) {
1133         free(sockcheck_conf.local_addr);
1134         sockcheck_conf.local_addr = NULL;
1135     }
1136     /* now try to read from the conf database */
1137     if ((my_node = conf_get_data("modules/sockcheck", RECDB_OBJECT))) {
1138         str = database_get_data(my_node, "max_sockets", RECDB_QSTRING);
1139         if (str) sockcheck_conf.max_clients = strtoul(str, NULL, 0);
1140         str = database_get_data(my_node, "max_clients", RECDB_QSTRING);
1141         if (str) sockcheck_conf.max_clients = strtoul(str, NULL, 0);
1142         str = database_get_data(my_node, "max_read", RECDB_QSTRING);
1143         if (str) sockcheck_conf.max_read = strtoul(str, NULL, 0);
1144         str = database_get_data(my_node, "max_cache_age", RECDB_QSTRING);
1145         if (str) sockcheck_conf.max_cache_age = ParseInterval(str);
1146         str = database_get_data(my_node, "gline_duration", RECDB_QSTRING);
1147         if (str) sockcheck_conf.gline_duration = ParseInterval(str);
1148         str = database_get_data(my_node, "address", RECDB_QSTRING);
1149         if (str) {
1150             struct sockaddr_in *sin;
1151             unsigned long addr;
1152
1153             sockcheck_conf.local_addr_len = sizeof(*sin);
1154             if (getipbyname(str, &addr)) {
1155                 sin = malloc(sockcheck_conf.local_addr_len);
1156                 sin->sin_family = AF_INET;
1157                 sin->sin_port = 0;
1158                 sin->sin_addr.s_addr = addr;
1159 #ifdef HAVE_SIN_LEN
1160                 sin->sin_len = 0;
1161 #endif
1162                 memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
1163                 sockcheck_conf.local_addr = sin;
1164             } else {
1165                 log_module(PC_LOG, LOG_ERROR, "Error: Unable to get host named `%s', not checking a specific address.", str);
1166                 sockcheck_conf.local_addr = NULL;
1167             }
1168         }
1169     }
1170 }
1171
1172 int
1173 sockcheck_init(void)
1174 {
1175     PC_LOG = log_register_type("ProxyCheck", "file:proxycheck.log");
1176     conf_register_reload(sockcheck_read_conf);
1177     reg_exit_func(sockcheck_shutdown);
1178     _sockcheck_init();
1179     message_register_table(msgtab);
1180
1181     sockcheck_module = module_register("ProxyCheck", PC_LOG, "mod-sockcheck.help", NULL);
1182     modcmd_register(sockcheck_module, "defproxy", cmd_defproxy, 2, 0, "level", "999", NULL);
1183     modcmd_register(sockcheck_module, "hostscan", cmd_hostscan, 2, 0, "level", "650", NULL);
1184     modcmd_register(sockcheck_module, "clearhost", cmd_clearhost, 2, 0, "level", "650", NULL);
1185     modcmd_register(sockcheck_module, "stats proxycheck", cmd_stats_proxycheck, 0, 0, NULL);
1186     reg_new_user_func(sockcheck_new_user);
1187     return 1;
1188 }
1189
1190 int
1191 sockcheck_finalize(void)
1192 {
1193     return 1;
1194 }