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.
23 /*----------------------------------------------------------------------------
25 *--------------------------------------------------------------------------*/
31 /*----------------------------------------------------------------------------
32 * Application Includes
33 *--------------------------------------------------------------------------*/
38 #include "ircd_alloc.h"
46 /*----------------------------------------------------------------------------
47 * Data Structures (should be moved to IPcheck.h)
48 *--------------------------------------------------------------------------*/
49 typedef struct IPTargetEntry {
51 unsigned char targets[MAXTARGETS];
54 typedef struct IPRegistryEntry {
55 struct IPRegistryEntry *next;
56 struct IPTargetEntry *target;
59 unsigned char connected;
60 unsigned char attempts;
65 * Hash table for IPv4 address registry
67 * Hash table size must be a power of 2
68 * Use 64K hash table to conserve memory
70 /*----------------------------------------------------------------------------
71 * Compile-time Configuration
72 *--------------------------------------------------------------------------*/
73 #define IP_REGISTRY_TABLE_SIZE 0x10000
74 #define MASK_16 0xffff
76 #define IPCHECK_CLONE_LIMIT 2
77 #define IPCHECK_CLONE_PERIOD 20
78 #define IPCHECK_CLONE_DELAY 600
81 /*----------------------------------------------------------------------------
83 *--------------------------------------------------------------------------*/
84 #define NOW (CurrentTime)
85 #define CONNECTED_SINCE(x) (NOW - (x->last_connect))
88 /*----------------------------------------------------------------------------
90 *--------------------------------------------------------------------------*/
91 static ip_reg_entry_t *hashTable[IP_REGISTRY_TABLE_SIZE];
92 static ip_reg_entry_t *freeList = 0;
95 /*----------------------------------------------------------------------------
96 * ip_registry_hash: Create a hash key for an IP registry entry and return
97 * the value. (Is unsigned int really a good type to give
98 * to the IP argument? Ugly. This should probably be a
99 * struct in_addr. This is asking for trouble. --ZS)
100 *--------------------------------------------------------------------------*/
101 static unsigned int ip_registry_hash(unsigned int ip) {
102 return ((ip >> 16) ^ ip) & (IP_REGISTRY_TABLE_SIZE - 1);
106 /*----------------------------------------------------------------------------
107 * ip_registry_find: Find a given IP registry entry and return it.
108 *--------------------------------------------------------------------------*/
109 static ip_reg_entry_t *ip_registry_find(unsigned int ip) {
110 ip_reg_entry_t *entry;
112 for (entry = hashTable[ip_registry_hash(ip)]; entry; entry = entry->next) {
113 if (entry->addr == ip)
121 /*----------------------------------------------------------------------------
122 * ip_registry_add: Add an entry to the IP registry
123 *--------------------------------------------------------------------------*/
124 static void ip_registry_add(ip_reg_entry_t *entry) {
125 unsigned int bucket = ip_registry_hash(entry->addr);
127 entry->next = hashTable[bucket];
128 hashTable[bucket] = entry;
132 /*----------------------------------------------------------------------------
133 * ip_registry_remove: Remove an entry from the IP registry
134 *--------------------------------------------------------------------------*/
135 static void ip_registry_remove(ip_reg_entry_t *entry) {
136 unsigned int bucket = ip_registry_hash(entry->addr);
138 if (hashTable[bucket] == entry)
139 hashTable[bucket] = entry->next;
141 ip_reg_entry_t *prev;
143 for (prev = hashTable[bucket]; prev; prev = prev->next) {
144 if (prev->next == entry) {
145 prev->next = entry->next;
153 /*----------------------------------------------------------------------------
154 * ip_registry_new_entry(): Creates and initializes an IP Registry entry.
155 * NOW ALSO ADDS IT TO THE LIST! --ZS
156 *--------------------------------------------------------------------------*/
157 static ip_reg_entry_t *ip_registry_new_entry(unsigned int addr, int attempt) {
158 ip_reg_entry_t *entry = freeList;
161 freeList = entry->next;
163 entry = (ip_reg_entry_t *)MyMalloc(sizeof(ip_reg_entry_t));
167 memset(entry, 0, sizeof(ip_reg_entry_t));
168 entry->last_connect = NOW; /* Seconds since last connect attempt */
169 entry->connected = 1; /* connected clients for this IP */
170 entry->attempts = attempt; /* Number attempts for this IP */
171 entry->addr = addr; /* Entry's IP Address */
173 ip_registry_add(entry);
179 /*----------------------------------------------------------------------------
180 * ip_registry_delete_entry: Frees an entry and adds the structure to a list
181 * of free structures. (We should probably reclaim
182 * the freelist every once in a while! This is
183 * potentially a way to DoS the server... -ZS)
184 *--------------------------------------------------------------------------*/
185 static void ip_registry_delete_entry(ip_reg_entry_t *entry) {
187 MyFree(entry->target);
189 entry->next = freeList;
194 /*----------------------------------------------------------------------------
195 * ip_registry_update_free_targets:
196 *--------------------------------------------------------------------------*/
197 static unsigned int ip_registry_update_free_targets(ip_reg_entry_t *entry) {
198 unsigned int free_targets = STARTTARGETS;
201 free_targets = (entry->target->count +
202 (CONNECTED_SINCE(entry) / TARGET_DELAY));
204 if (free_targets > STARTTARGETS)
205 free_targets = STARTTARGETS;
207 entry->target->count = free_targets;
214 /*----------------------------------------------------------------------------
215 * ip_registry_expire_entry: expire an IP entry if it needs to be. If an
216 * entry isn't expired, then also check the target
217 * list to see if it needs to be expired.
218 *--------------------------------------------------------------------------*/
219 static void ip_registry_expire_entry(ip_reg_entry_t *entry) {
221 * Don't touch this number, it has statistical significance
222 * XXX - blah blah blah
223 * ZS - Just -what- statistical significance does it -have-?
225 if (CONNECTED_SINCE(entry) > 600) {
226 ip_registry_remove(entry);
227 ip_registry_delete_entry(entry);
228 } else if (CONNECTED_SINCE(entry) > 120 && 0 != entry->target) {
229 MyFree(entry->target);
235 /*----------------------------------------------------------------------------
236 * ip_registry_expire: Expire all of the needed entries in the hash table
237 *--------------------------------------------------------------------------*/
238 void ip_registry_expire(void) {
239 ip_reg_entry_t *entry;
240 ip_reg_entry_t *entry_next;
241 static time_t next_expire = 0;
244 /* Only do this if we're ready to */
245 if (next_expire >= CurrentTime)
248 for (i = 0; i < IP_REGISTRY_TABLE_SIZE; ++i) {
249 for (entry = hashTable[i]; entry; entry = entry_next) {
250 entry_next = entry->next;
251 if (0 == entry->connected)
252 ip_registry_expire_entry(entry);
256 next_expire = CurrentTime + 60;
260 /*----------------------------------------------------------------------------
261 * IPcheck_local_connect
264 * A new connection was accept()-ed with IP number `cptr->ip.s_addr'.
267 * Update the IPcheck registry.
269 * 1 : You're allowed to connect.
270 * 0 : You're not allowed to connect.
274 * A connection should be rejected when a connection from the same IP
275 * number was received IPCHECK_CLONE_LIMIT times before this connect
276 * attempt, with reconnect intervals of IPCHECK_CLONE_PERIOD seconds
279 * Free target inheritance:
281 * When the client is accepted, then the number of Free Targets
282 * of the cptr is set to the value stored in the found IPregistry
283 * structure, or left at STARTTARGETS. This can be done by changing
284 * cptr->nexttarget to be `now - (TARGET_DELAY * (FREE_TARGETS - 1))',
285 * where FREE_TARGETS may range from 0 till STARTTARGETS.
286 *--------------------------------------------------------------------------*/
287 int ip_registry_check_local(unsigned int addr, time_t *next_target_out)
289 ip_reg_entry_t *entry = ip_registry_find(addr);
290 unsigned int free_targets = STARTTARGETS;
292 assert(0 != next_target_out);
295 entry = ip_registry_new_entry(addr, 1);
299 /* Do not allow more than 255 connects from a single IP, EVER. */
300 if (0 == ++entry->connected)
303 /* If our threshhold has elapsed, reset the counter so we don't throttle */
304 if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
306 else if (0 == ++entry->attempts)
307 --entry->attempts; /* Disallow overflow */
309 entry->last_connect = NOW;
310 free_targets = ip_registry_update_free_targets(entry);
312 if (entry->attempts < IPCHECK_CLONE_LIMIT && next_target_out)
313 *next_target_out = CurrentTime - (TARGET_DELAY * free_targets - 1);
314 else if ((CurrentTime - me.since) > IPCHECK_CLONE_DELAY) {
327 /*----------------------------------------------------------------------------
328 * ip_registry_local_connect
330 * Does anything that needs to be done once we actually have a client
331 * structure to play with on a local connection that passed the IPcheck test.
332 *--------------------------------------------------------------------------*/
333 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) {
351 ip_reg_entry_t *entry;
355 entry = ip_registry_find(cptr->ip.s_addr);
360 entry = ip_registry_new_entry(cptr->ip.s_addr, (is_burst ? 0 : 1));
362 /* NEVER more than 255 connections. */
363 if (0 == ++entry->connected)
366 /* Make sure we don't bounce if our threshhold has expired */
367 if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
370 /* If we're not part of a burst, go ahead and process the rest */
372 if (0 == ++entry->attempts)
373 --entry->attempts; /* Overflows are bad, mmmkay? */
374 ip_registry_update_free_targets(entry);
375 entry->last_connect = NOW;
382 /*----------------------------------------------------------------------------
383 * IPcheck_connect_fail
386 * This local client failed to connect due to legal reasons.
389 * Neutralize the effect of calling IPcheck_local_connect, in such
390 * a way that the client won't be penalized when trying to reconnect
392 *--------------------------------------------------------------------------*/
393 void ip_registry_connect_fail(unsigned int addr) {
394 ip_reg_entry_t *entry = ip_registry_find(addr);
401 /*----------------------------------------------------------------------------
402 * IPcheck_connect_succeeded
405 * A client succeeded to finish the registration.
407 * Finish IPcheck registration of a successfully, locally connected client.
408 *--------------------------------------------------------------------------*/
409 void ip_registry_connect_succeeded(struct Client *cptr) {
411 unsigned int free_targets = STARTTARGETS;
412 ip_reg_entry_t *entry;
416 entry = ip_registry_find(cptr->ip.s_addr);
419 Debug((DEBUG_ERROR, "Missing registry entry for: %s", cptr->sock_ip));
424 memcpy(cptr->targets, entry->target->targets, MAXTARGETS);
425 free_targets = entry->target->count;
429 sendcmdto_one(&me, CMD_NOTICE, cptr, "%C :on %u ca %u(%u) ft %u(%u)%s",
430 cptr, entry->connected, entry->attempts, IPCHECK_CLONE_LIMIT,
431 free_targets, STARTTARGETS, tr);
435 /*----------------------------------------------------------------------------
439 * A local client disconnected or a remote client left Undernet.
442 * Update the IPcheck registry.
443 * Remove all expired IPregistry structures from the hash bucket
444 * that belongs to this clients IP number.
445 *--------------------------------------------------------------------------*/
446 void ip_registry_disconnect(struct Client *cptr) {
447 ip_reg_entry_t *entry;
451 if (!IsIPChecked(cptr))
454 entry = ip_registry_find(cptr->ip.s_addr);
456 /* Entry is probably a server if this happens. */
462 * If this was the last one, set `last_connect' to disconnect time
463 * (used for expiration) Note that we reset attempts here as well if our
464 * threshhold hasn't been crossed.
466 if (0 == --entry->connected) {
467 if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD)
469 ip_registry_update_free_targets(entry);
470 entry->last_connect = NOW;
474 if (MyConnect(cptr)) {
475 unsigned int free_targets;
477 if (0 == entry->target) {
478 entry->target = (iptarget_entry_t *)MyMalloc(sizeof(iptarget_entry_t));
479 assert(0 != entry->target);
480 entry->target->count = STARTTARGETS;
482 memcpy(entry->target->targets, cptr->targets, MAXTARGETS);
485 * This calculation can be pretty unfair towards large multi-user hosts,
486 * but there is "nothing" we can do without also allowing spam bots to
487 * send more messages or by drastically increasing the ammount of memory
488 * used in the IPregistry.
490 * The problem is that when a client disconnects, leaving no free targets,
491 * then the next client from that IP number has to pay for it (getting no
492 * free targets). But ALSO the next client, and the next client, and the
493 * next client etc - until another client disconnects that DOES leave free
494 * targets. The reason for this is that if there are 10 SPAM bots, and
495 * they all disconnect at once, then they ALL should get no free targets
496 * when reconnecting. We'd need to store an entry per client (instead of
497 * per IP number) to avoid this.
499 if (cptr->nexttarget < CurrentTime)
500 free_targets = (CurrentTime - cptr->nexttarget) / TARGET_DELAY + 1;
504 /* Add bonus, this is pretty fuzzy, but it will help in some cases. */
505 if ((CurrentTime - cptr->firsttime) > 600)
506 free_targets += (CurrentTime - cptr->firsttime - 600) / TARGET_DELAY;
508 /* Finally, store smallest value for Judgement Day */
509 if (free_targets < entry->target->count)
510 entry->target->count = free_targets;
514 /*----------------------------------------------------------------------------
517 * Returns number of clients with the same IP number
518 *--------------------------------------------------------------------------*/
519 int ip_registry_count(unsigned int addr) {
520 ip_reg_entry_t *entry = ip_registry_find(addr);
521 return (entry) ? entry->connected : 0;