2 * IRC - Internet Relay Chat, ircd/IPcheck.c
3 * Copyright (C) 1998 Carlo Wood ( Run @ undernet.org )
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "ircd_alloc.h"
38 struct IPTargetEntry {
40 unsigned char targets[MAXTARGETS];
43 struct IPRegistryEntry {
44 struct IPRegistryEntry *next;
45 struct IPTargetEntry *target;
48 unsigned char connected;
49 unsigned char attempts;
54 * Hash table for IPv4 address registry
56 * Hash table size must be a power of 2
57 * Use 64K hash table to conserve memory
59 /*----------------------------------------------------------------------------
60 * Compile-time Configuration
61 *--------------------------------------------------------------------------*/
62 #define IP_REGISTRY_TABLE_SIZE 0x10000
63 #define MASK_16 0xffff
65 #define IPCHECK_CLONE_LIMIT 2
66 #define IPCHECK_CLONE_PERIOD 20
67 #define IPCHECK_CLONE_DELAY 600
70 /*----------------------------------------------------------------------------
72 *--------------------------------------------------------------------------*/
73 #define NOW (CurrentTime)
74 #define CONNECTED_SINCE(x) (NOW - (x->last_connect))
77 /*----------------------------------------------------------------------------
79 *--------------------------------------------------------------------------*/
80 static struct IPRegistryEntry *hashTable[IP_REGISTRY_TABLE_SIZE];
81 static struct IPRegistryEntry *freeList = 0;
84 /*----------------------------------------------------------------------------
85 * ip_registry_hash: Create a hash key for an IP registry entry and return
86 * the value. (Is unsigned int really a good type to give
87 * to the IP argument? Ugly. This should probably be a
88 * struct in_addr. This is asking for trouble. --ZS)
89 *--------------------------------------------------------------------------*/
90 static unsigned int ip_registry_hash(unsigned int ip)
92 return ((ip >> 16) ^ ip) & (IP_REGISTRY_TABLE_SIZE - 1);
96 /*----------------------------------------------------------------------------
97 * ip_registry_find: Find a given IP registry entry and return it.
98 *--------------------------------------------------------------------------*/
99 static struct IPRegistryEntry *ip_registry_find(unsigned int ip)
101 struct IPRegistryEntry *entry;
103 for (entry = hashTable[ip_registry_hash(ip)]; entry; entry = entry->next) {
104 if (entry->addr == ip)
112 /*----------------------------------------------------------------------------
113 * ip_registry_add: Add an entry to the IP registry
114 *--------------------------------------------------------------------------*/
115 static void ip_registry_add(struct IPRegistryEntry *entry)
117 unsigned int bucket = ip_registry_hash(entry->addr);
119 entry->next = hashTable[bucket];
120 hashTable[bucket] = entry;
124 /*----------------------------------------------------------------------------
125 * ip_registry_remove: Remove an entry from the IP registry
126 *--------------------------------------------------------------------------*/
127 static void ip_registry_remove(struct IPRegistryEntry* entry)
129 unsigned int bucket = ip_registry_hash(entry->addr);
131 if (hashTable[bucket] == entry)
132 hashTable[bucket] = entry->next;
134 struct IPRegistryEntry *prev;
136 for (prev = hashTable[bucket]; prev; prev = prev->next) {
137 if (prev->next == entry) {
138 prev->next = entry->next;
146 /*----------------------------------------------------------------------------
147 * ip_registry_new_entry(): Creates and initializes an IP Registry entry.
148 * NOW ALSO ADDS IT TO THE LIST! --ZS
149 *--------------------------------------------------------------------------*/
150 static struct IPRegistryEntry *ip_registry_new_entry(unsigned int addr, int attempt)
152 struct IPRegistryEntry* entry = freeList;
155 freeList = entry->next;
157 entry = (struct IPRegistryEntry *)MyMalloc(sizeof(struct IPRegistryEntry));
161 memset(entry, 0, sizeof(struct IPRegistryEntry));
162 entry->last_connect = NOW; /* Seconds since last connect attempt */
163 entry->connected = 1; /* connected clients for this IP */
164 entry->attempts = attempt; /* Number attempts for this IP */
165 entry->addr = addr; /* Entry's IP Address */
167 ip_registry_add(entry);
173 /*----------------------------------------------------------------------------
174 * ip_registry_delete_entry: Frees an entry and adds the structure to a list
175 * of free structures. (We should probably reclaim
176 * the freelist every once in a while! This is
177 * potentially a way to DoS the server... -ZS)
178 *--------------------------------------------------------------------------*/
179 static void ip_registry_delete_entry(struct IPRegistryEntry *entry)
182 MyFree(entry->target);
184 entry->next = freeList;
189 /*----------------------------------------------------------------------------
190 * ip_registry_update_free_targets:
191 *--------------------------------------------------------------------------*/
192 static unsigned int ip_registry_update_free_targets(struct IPRegistryEntry *entry)
194 unsigned int free_targets = STARTTARGETS;
197 free_targets = (entry->target->count +
198 (CONNECTED_SINCE(entry) / TARGET_DELAY));
200 if (free_targets > STARTTARGETS)
201 free_targets = STARTTARGETS;
203 entry->target->count = free_targets;
210 /*----------------------------------------------------------------------------
211 * ip_registry_expire_entry: expire an IP entry if it needs to be. If an
212 * entry isn't expired, then also check the target
213 * list to see if it needs to be expired.
214 *--------------------------------------------------------------------------*/
215 static void ip_registry_expire_entry(struct IPRegistryEntry *entry)
218 * Don't touch this number, it has statistical significance
219 * XXX - blah blah blah
220 * ZS - Just -what- statistical significance does it -have-?
222 if (CONNECTED_SINCE(entry) > 600) {
223 ip_registry_remove(entry);
224 ip_registry_delete_entry(entry);
226 else if (CONNECTED_SINCE(entry) > 120 && 0 != entry->target) {
227 MyFree(entry->target);
233 /*----------------------------------------------------------------------------
234 * ip_registry_expire: Expire all of the needed entries in the hash table
235 *--------------------------------------------------------------------------*/
236 void ip_registry_expire(void)
238 struct IPRegistryEntry *entry;
239 struct IPRegistryEntry *entry_next;
240 static time_t next_expire = 0;
243 /* Only do this if we're ready to */
244 if (next_expire >= CurrentTime)
247 for (i = 0; i < IP_REGISTRY_TABLE_SIZE; ++i) {
248 for (entry = hashTable[i]; entry; entry = entry_next) {
249 entry_next = entry->next;
250 if (0 == entry->connected)
251 ip_registry_expire_entry(entry);
255 next_expire = CurrentTime + 60;
259 /*----------------------------------------------------------------------------
260 * IPcheck_local_connect
263 * A new connection was accept()-ed with IP number `cptr->ip.s_addr'.
266 * Update the IPcheck registry.
268 * 1 : You're allowed to connect.
269 * 0 : You're not allowed to connect.
273 * A connection should be rejected when a connection from the same IP
274 * number was received IPCHECK_CLONE_LIMIT times before this connect
275 * attempt, with reconnect intervals of IPCHECK_CLONE_PERIOD seconds
278 * Free target inheritance:
280 * When the client is accepted, then the number of Free Targets
281 * of the cptr is set to the value stored in the found IPregistry
282 * structure, or left at STARTTARGETS. This can be done by changing
283 * cptr->nexttarget to be `now - (TARGET_DELAY * (FREE_TARGETS - 1))',
284 * where FREE_TARGETS may range from 0 till STARTTARGETS.
285 *--------------------------------------------------------------------------*/
286 int ip_registry_check_local(unsigned int addr, time_t *next_target_out)
288 struct IPRegistryEntry *entry = ip_registry_find(addr);
289 unsigned int free_targets = STARTTARGETS;
291 assert(0 != next_target_out);
294 entry = ip_registry_new_entry(addr, 1);
298 /* Do not allow more than 255 connects from a single IP, EVER. */
299 if (0 == ++entry->connected)
302 /* If our threshhold has elapsed, reset the counter so we don't throttle */
303 if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
305 else if (0 == ++entry->attempts)
306 --entry->attempts; /* Disallow overflow */
308 entry->last_connect = NOW;
309 free_targets = ip_registry_update_free_targets(entry);
311 if (entry->attempts < IPCHECK_CLONE_LIMIT && next_target_out)
312 *next_target_out = CurrentTime - (TARGET_DELAY * free_targets - 1);
313 else if ((CurrentTime - me.since) > IPCHECK_CLONE_DELAY) {
326 /*----------------------------------------------------------------------------
327 * ip_registry_local_connect
329 * Does anything that needs to be done once we actually have a client
330 * structure to play with on a local connection that passed the IPcheck test.
331 *--------------------------------------------------------------------------*/
332 void ip_registry_local_connect(struct Client *cptr)
339 /*----------------------------------------------------------------------------
340 * IPcheck_remote_connect
343 * A remote client connected to Undernet, with IP number `cptr->ip.s_addr'
344 * and hostname `hostname'.
347 * Update the IPcheck registry.
348 * Return 0 on failure, 1 on success.
349 *--------------------------------------------------------------------------*/
350 int ip_registry_check_remote(struct Client* cptr, int is_burst)
352 struct IPRegistryEntry *entry;
356 entry = ip_registry_find(cptr->ip.s_addr);
361 entry = ip_registry_new_entry(cptr->ip.s_addr, (is_burst ? 0 : 1));
363 /* NEVER more than 255 connections. */
364 if (0 == ++entry->connected)
367 /* Make sure we don't bounce if our threshhold has expired */
368 if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
371 /* If we're not part of a burst, go ahead and process the rest */
373 if (0 == ++entry->attempts)
374 --entry->attempts; /* Overflows are bad, mmmkay? */
375 ip_registry_update_free_targets(entry);
376 entry->last_connect = NOW;
383 /*----------------------------------------------------------------------------
384 * IPcheck_connect_fail
387 * This local client failed to connect due to legal reasons.
390 * Neutralize the effect of calling IPcheck_local_connect, in such
391 * a way that the client won't be penalized when trying to reconnect
393 *--------------------------------------------------------------------------*/
394 void ip_registry_connect_fail(unsigned int addr)
396 struct IPRegistryEntry *entry = ip_registry_find(addr);
403 /*----------------------------------------------------------------------------
404 * IPcheck_connect_succeeded
407 * A client succeeded to finish the registration.
409 * Finish IPcheck registration of a successfully, locally connected client.
410 *--------------------------------------------------------------------------*/
411 void ip_registry_connect_succeeded(struct Client *cptr)
414 unsigned int free_targets = STARTTARGETS;
415 struct IPRegistryEntry *entry;
419 entry = ip_registry_find(cptr->ip.s_addr);
422 Debug((DEBUG_ERROR, "Missing registry entry for: %s", cptr->sock_ip));
427 memcpy(cptr->targets, entry->target->targets, MAXTARGETS);
428 free_targets = entry->target->count;
432 sendcmdto_one(&me, CMD_NOTICE, cptr, "%C :on %u ca %u(%u) ft %u(%u)%s",
433 cptr, entry->connected, entry->attempts, IPCHECK_CLONE_LIMIT,
434 free_targets, STARTTARGETS, tr);
438 /*----------------------------------------------------------------------------
442 * A local client disconnected or a remote client left Undernet.
445 * Update the IPcheck registry.
446 * Remove all expired IPregistry structures from the hash bucket
447 * that belongs to this clients IP number.
448 *--------------------------------------------------------------------------*/
449 void ip_registry_disconnect(struct Client *cptr)
451 struct IPRegistryEntry *entry;
455 if (!IsIPChecked(cptr))
458 entry = ip_registry_find(cptr->ip.s_addr);
460 /* Entry is probably a server if this happens. */
466 * If this was the last one, set `last_connect' to disconnect time
467 * (used for expiration) Note that we reset attempts here as well if our
468 * threshhold hasn't been crossed.
470 if (0 == --entry->connected) {
471 if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD)
473 ip_registry_update_free_targets(entry);
474 entry->last_connect = NOW;
478 if (MyConnect(cptr)) {
479 unsigned int free_targets;
481 if (0 == entry->target) {
482 entry->target = (struct IPTargetEntry *)MyMalloc(sizeof(struct IPTargetEntry));
483 assert(0 != entry->target);
484 entry->target->count = STARTTARGETS;
486 memcpy(entry->target->targets, cptr->targets, MAXTARGETS);
489 * This calculation can be pretty unfair towards large multi-user hosts,
490 * but there is "nothing" we can do without also allowing spam bots to
491 * send more messages or by drastically increasing the ammount of memory
492 * used in the IPregistry.
494 * The problem is that when a client disconnects, leaving no free targets,
495 * then the next client from that IP number has to pay for it (getting no
496 * free targets). But ALSO the next client, and the next client, and the
497 * next client etc - until another client disconnects that DOES leave free
498 * targets. The reason for this is that if there are 10 SPAM bots, and
499 * they all disconnect at once, then they ALL should get no free targets
500 * when reconnecting. We'd need to store an entry per client (instead of
501 * per IP number) to avoid this.
503 if (cptr->nexttarget < CurrentTime)
504 free_targets = (CurrentTime - cptr->nexttarget) / TARGET_DELAY + 1;
508 /* Add bonus, this is pretty fuzzy, but it will help in some cases. */
509 if ((CurrentTime - cptr->firsttime) > 600)
510 free_targets += (CurrentTime - cptr->firsttime - 600) / TARGET_DELAY;
512 /* Finally, store smallest value for Judgement Day */
513 if (free_targets < entry->target->count)
514 entry->target->count = free_targets;
518 /*----------------------------------------------------------------------------
521 * Returns number of clients with the same IP number
522 *--------------------------------------------------------------------------*/
523 int ip_registry_count(unsigned int addr)
525 struct IPRegistryEntry *entry = ip_registry_find(addr);
526 return (entry) ? entry->connected : 0;