| 1 |
/* |
| 2 |
Copyright (C) 2002-2003 Andy Smith |
| 3 |
|
| 4 |
This program is free software; you can redistribute it and/or |
| 5 |
modify it under the terms of the GNU General Public License |
| 6 |
as published by the Free Software Foundation; either version 2 |
| 7 |
of the License, or (at your option) any later version. |
| 8 |
|
| 9 |
This program is distributed in the hope that it will be useful, |
| 10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 |
GNU General Public License for more details. |
| 13 |
|
| 14 |
You should have received a copy of the GNU General Public License |
| 15 |
along with this program; if not, write to: |
| 16 |
|
| 17 |
the Free Software Foundation, Inc. |
| 18 |
59 Temple Place - Suite 330 |
| 19 |
Boston, MA 02111-1307, USA. |
| 20 |
|
| 21 |
*/ |
| 22 |
|
| 23 |
/* |
| 24 |
* A Negative caching implementation for IPv4 addresses. The idea is that |
| 25 |
* every time an IP address is seen, it is checked against a patricia trie. If |
| 26 |
* the IP address was previously seen and within an acceptable period of time, |
| 27 |
* it is not scanned again. Otherwise, the address is scanned as normal. If |
| 28 |
* it is proven to be OK (i.e. it doesn't run an open proxy) then it is added |
| 29 |
* to the trie. |
| 30 |
* |
| 31 |
* I'm using a patricia trie as described on p253 of Sedgewick. The reason for |
| 32 |
* this is that it can find any IP address with only log_2 N bit comparisons |
| 33 |
* and only requires as many nodes as we have IP addresses to store. |
| 34 |
* |
| 35 |
* I would have liked to have made the trie implementation generic but couldn't |
| 36 |
* see how to do it easily given that the trie structure depends on the data |
| 37 |
* within it. |
| 38 |
* --grifferz |
| 39 |
*/ |
| 40 |
|
| 41 |
#include "setup.h" |
| 42 |
|
| 43 |
#include <stdio.h> |
| 44 |
#include <stdlib.h> |
| 45 |
|
| 46 |
#ifdef TIME_WITH_SYS_TIME |
| 47 |
#include <sys/time.h> |
| 48 |
#include <time.h> |
| 49 |
#else |
| 50 |
# ifdef HAVE_SYS_TIME_H |
| 51 |
# include <sys/time.h> |
| 52 |
# else |
| 53 |
# include <time.h> |
| 54 |
# endif |
| 55 |
#endif |
| 56 |
|
| 57 |
#include "irc.h" |
| 58 |
#include "negcache.h" |
| 59 |
#include "config.h" |
| 60 |
#include "malloc.h" |
| 61 |
#include "log.h" |
| 62 |
|
| 63 |
|
| 64 |
extern unsigned int OPT_DEBUG; |
| 65 |
|
| 66 |
static struct cnode *nc_search(struct cnode *head, const unsigned long ip); |
| 67 |
static struct cnode *nc_insert(struct cnode *head, const unsigned long ip); |
| 68 |
static void nc_rebuild(struct cnode *old_head, struct cnode *new_head, |
| 69 |
struct cnode *n, time_t now); |
| 70 |
|
| 71 |
time_t last_nc_expire; |
| 72 |
unsigned int maxb; |
| 73 |
extern struct cnode *nc_head; |
| 74 |
|
| 75 |
/* |
| 76 |
* Return the bit which appears k bits from the right in x. |
| 77 |
*/ |
| 78 |
#define GETBIT(x,k) ((x >> k) & 1) |
| 79 |
|
| 80 |
|
| 81 |
/* |
| 82 |
* Initialise the patricia trie we use for storing our negative cache. |
| 83 |
*/ |
| 84 |
void nc_init(struct cnode **head) |
| 85 |
{ |
| 86 |
if (*head) |
| 87 |
{ |
| 88 |
/* Cache already exists */ |
| 89 |
return; |
| 90 |
} |
| 91 |
|
| 92 |
*head = MyMalloc(sizeof **head); |
| 93 |
|
| 94 |
maxb = (sizeof((*head)->ip) * 8); |
| 95 |
(*head)->ip = 0; |
| 96 |
(*head)->b = maxb; |
| 97 |
(*head)->l = (*head)->r = *head; |
| 98 |
last_nc_expire = time(NULL); |
| 99 |
} |
| 100 |
|
| 101 |
/* |
| 102 |
* Find and return a pointer to the trie node that corresponds to a given IP |
| 103 |
* address as expressed in network form, or return NULL if the IP isn't |
| 104 |
* present. |
| 105 |
*/ |
| 106 |
static struct cnode *nc_search(struct cnode *head, const unsigned long ip) |
| 107 |
{ |
| 108 |
struct cnode *p, *x; |
| 109 |
|
| 110 |
p = head; |
| 111 |
x = head->l; |
| 112 |
|
| 113 |
while (p->b > x->b) |
| 114 |
{ |
| 115 |
p = x; |
| 116 |
x = GETBIT(ip, x->b) ? x->r : x->l; |
| 117 |
} |
| 118 |
|
| 119 |
if (ip == x->ip) |
| 120 |
return(x); |
| 121 |
else |
| 122 |
return(NULL); |
| 123 |
} |
| 124 |
|
| 125 |
/* |
| 126 |
* Insert a new node into the trie, and return a pointer to it. |
| 127 |
*/ |
| 128 |
static struct cnode *nc_insert(struct cnode *head, const unsigned long ip) |
| 129 |
{ |
| 130 |
unsigned int i; |
| 131 |
struct cnode *p, *t, *x; |
| 132 |
|
| 133 |
i = maxb; |
| 134 |
p = head; |
| 135 |
t = head->l; |
| 136 |
|
| 137 |
while (p->b > t->b) |
| 138 |
{ |
| 139 |
p = t; |
| 140 |
t = GETBIT(ip, t->b) ? t->r : t->l; |
| 141 |
} |
| 142 |
|
| 143 |
if (ip == t->ip) |
| 144 |
{ |
| 145 |
/* Node already exists. */ |
| 146 |
return(t); |
| 147 |
} |
| 148 |
|
| 149 |
while (GETBIT(t->ip, i) == GETBIT(ip, i)) |
| 150 |
i--; |
| 151 |
|
| 152 |
p = head; |
| 153 |
x = head->l; |
| 154 |
|
| 155 |
while (p->b > x->b && x->b > i) |
| 156 |
{ |
| 157 |
p = x; |
| 158 |
x = GETBIT(ip, x->b) ? x->r : x->l; |
| 159 |
} |
| 160 |
|
| 161 |
t = MyMalloc(sizeof *t); |
| 162 |
t->ip = ip; |
| 163 |
t->b = i; |
| 164 |
t->l = GETBIT(ip, t->b) ? x : t; |
| 165 |
t->r = GETBIT(ip, t->b) ? t : x; |
| 166 |
|
| 167 |
if (GETBIT(ip, p->b)) |
| 168 |
p->r = t; |
| 169 |
else |
| 170 |
p->l = t; |
| 171 |
|
| 172 |
return(t); |
| 173 |
} |
| 174 |
|
| 175 |
/* |
| 176 |
* Check whether an IP address is in our negative cache and was added |
| 177 |
* recently enough. Return a pointer to its node if so, NULL otherwise. |
| 178 |
*/ |
| 179 |
struct cnode *check_neg_cache(const unsigned long ip) |
| 180 |
{ |
| 181 |
time_t now; |
| 182 |
struct cnode *n; |
| 183 |
|
| 184 |
if (OptionsItem->negcache <= 0) |
| 185 |
return(NULL); |
| 186 |
|
| 187 |
n = nc_search(nc_head, ip); |
| 188 |
|
| 189 |
if (n) |
| 190 |
{ |
| 191 |
/* Check it is recent enough. */ |
| 192 |
now = time(NULL); |
| 193 |
|
| 194 |
if (now - n->seen <= OptionsItem->negcache) |
| 195 |
return(n); |
| 196 |
} |
| 197 |
|
| 198 |
return(NULL); |
| 199 |
} |
| 200 |
|
| 201 |
/* |
| 202 |
* Prepare an ASCII string representing an IPv4 address for inserting into |
| 203 |
* our negative cache. |
| 204 |
*/ |
| 205 |
void negcache_insert(const char *ipstr) |
| 206 |
{ |
| 207 |
struct bopm_sockaddr ip; |
| 208 |
struct cnode *n; |
| 209 |
|
| 210 |
if (inet_pton(AF_INET, ipstr, &(ip.sa4.sin_addr)) <= 0) |
| 211 |
{ |
| 212 |
log_printf("NEGCACHE -> Invalid IPv4 address '%s'", ipstr); |
| 213 |
return; |
| 214 |
} |
| 215 |
|
| 216 |
n = nc_insert(nc_head, ip.sa4.sin_addr.s_addr); |
| 217 |
|
| 218 |
if (n) |
| 219 |
n->seen = time(NULL); |
| 220 |
} |
| 221 |
|
| 222 |
/* |
| 223 |
* Recursively walk the cache and insert the nodes into a new patricia trie, |
| 224 |
* skipping nodes that are too old. |
| 225 |
*/ |
| 226 |
static void nc_rebuild(struct cnode *old_head, struct cnode *new_head, |
| 227 |
struct cnode *n, time_t now) |
| 228 |
{ |
| 229 |
struct cnode *new; |
| 230 |
|
| 231 |
if (!n) |
| 232 |
{ |
| 233 |
/* Start at head. */ |
| 234 |
n = old_head->l; |
| 235 |
} |
| 236 |
|
| 237 |
if (n == old_head) |
| 238 |
{ |
| 239 |
/* Trie is empty. */ |
| 240 |
return; |
| 241 |
} |
| 242 |
|
| 243 |
if (n->b > n->l->b) |
| 244 |
{ |
| 245 |
/* |
| 246 |
* If the trie extends via the left branch, follow it |
| 247 |
* recursively. |
| 248 |
*/ |
| 249 |
nc_rebuild(old_head, new_head, n->l, now); |
| 250 |
} |
| 251 |
|
| 252 |
if (n->b > n->r->b) |
| 253 |
{ |
| 254 |
/* |
| 255 |
* If the trie extends via the right branch, follow it |
| 256 |
* recursively. |
| 257 |
*/ |
| 258 |
nc_rebuild(old_head, new_head, n->r, now); |
| 259 |
} |
| 260 |
|
| 261 |
if ((now - n->seen) < OptionsItem->negcache) |
| 262 |
{ |
| 263 |
/* |
| 264 |
* We want to keep this node, so insert it into the new |
| 265 |
* trie. |
| 266 |
*/ |
| 267 |
new = nc_insert(new_head, n->ip); |
| 268 |
new->seen = n->seen; |
| 269 |
} |
| 270 |
else if (OPT_DEBUG >= 2) |
| 271 |
{ |
| 272 |
log_printf("NEGCACHE -> Deleting negcache node for %lu added at %lu", n->ip, |
| 273 |
n->seen); |
| 274 |
} |
| 275 |
|
| 276 |
/* Safe to free() this node now. */ |
| 277 |
MyFree(n); |
| 278 |
} |
| 279 |
|
| 280 |
/* |
| 281 |
* Wrapper for recursive rebuild function. |
| 282 |
*/ |
| 283 |
void negcache_rebuild(void) |
| 284 |
{ |
| 285 |
time_t now; |
| 286 |
struct cnode *new_head; |
| 287 |
|
| 288 |
now = time(NULL); |
| 289 |
new_head = NULL; |
| 290 |
|
| 291 |
nc_init(&new_head); |
| 292 |
nc_rebuild(nc_head, new_head, NULL, now); |
| 293 |
MyFree(nc_head); |
| 294 |
nc_head = new_head; |
| 295 |
} |