Author: Bleep <tomh@inxpress.net>
[ircu2.10.12-pk.git] / ircd / IPcheck.c
1 /*
2  * IRC - Internet Relay Chat, ircd/IPcheck.c
3  * Copyright (C) 1998 Carlo Wood ( Run @ undernet.org )
4  *
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)
8  * any later version.
9  *
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.
14  *
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.
18  *
19  * $Id$
20  *
21  */
22 #include "IPcheck.h"
23 #include "client.h"
24 #include "ircd.h"
25 #include "numnicks.h"
26 #include "ircd_alloc.h"
27 #include "msg.h"
28 #include "s_bsd.h"
29 #include "s_debug.h"
30 #include "s_user.h"
31 #include "send.h"
32
33 #include <assert.h>
34 #include <stdio.h>
35 #include <string.h>
36
37
38 struct IPTargetEntry {
39   int           count;
40   unsigned char targets[MAXTARGETS];
41 };
42
43 struct IPRegistryEntry {
44   struct IPRegistryEntry *next;
45   struct IPTargetEntry   *target;
46   unsigned int             addr;
47   time_t                  last_connect;
48   unsigned char            connected;
49   unsigned char            attempts;
50 };
51
52
53 /*
54  * Hash table for IPv4 address registry
55  *
56  * Hash table size must be a power of 2
57  * Use 64K hash table to conserve memory
58  */
59 /*----------------------------------------------------------------------------
60  * Compile-time Configuration
61  *--------------------------------------------------------------------------*/
62 #define IP_REGISTRY_TABLE_SIZE 0x10000
63 #define MASK_16                0xffff
64
65 #define IPCHECK_CLONE_LIMIT 2
66 #define IPCHECK_CLONE_PERIOD 20
67 #define IPCHECK_CLONE_DELAY  600
68
69
70 /*----------------------------------------------------------------------------
71  * Handy Macros
72  *--------------------------------------------------------------------------*/
73 #define NOW (CurrentTime)
74 #define CONNECTED_SINCE(x) (NOW - (x->last_connect))
75
76
77 /*----------------------------------------------------------------------------
78  * Global Data (ugly!)
79  *--------------------------------------------------------------------------*/
80 static struct IPRegistryEntry *hashTable[IP_REGISTRY_TABLE_SIZE];
81 static struct IPRegistryEntry *freeList = 0;
82
83
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)
91 {
92   return ((ip >> 16) ^ ip) & (IP_REGISTRY_TABLE_SIZE - 1);
93 }
94
95
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) 
100 {
101   struct IPRegistryEntry *entry;
102
103   for (entry = hashTable[ip_registry_hash(ip)]; entry; entry = entry->next) {
104     if (entry->addr == ip)
105       break;
106   }
107
108   return entry;
109 }
110
111
112 /*----------------------------------------------------------------------------
113  * ip_registry_add:  Add an entry to the IP registry
114  *--------------------------------------------------------------------------*/
115 static void ip_registry_add(struct IPRegistryEntry *entry) 
116 {
117   unsigned int bucket = ip_registry_hash(entry->addr);
118
119   entry->next = hashTable[bucket];
120   hashTable[bucket] = entry;
121 }
122   
123
124 /*----------------------------------------------------------------------------
125  * ip_registry_remove:  Remove an entry from the IP registry
126  *--------------------------------------------------------------------------*/
127 static void ip_registry_remove(struct IPRegistryEntry* entry) 
128 {
129   unsigned int bucket = ip_registry_hash(entry->addr);
130
131   if (hashTable[bucket] == entry)
132     hashTable[bucket] = entry->next;
133   else {
134     struct IPRegistryEntry *prev;
135
136     for (prev = hashTable[bucket]; prev; prev = prev->next) {
137       if (prev->next == entry) {
138         prev->next = entry->next;
139         break;
140       }
141     }
142   }
143 }
144  
145
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)
151 {
152   struct IPRegistryEntry* entry = freeList;
153
154   if (entry)
155     freeList = entry->next;
156   else
157     entry = (struct IPRegistryEntry *)MyMalloc(sizeof(struct IPRegistryEntry));
158
159   assert(0 != entry);
160
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                 */
166
167   ip_registry_add(entry);
168
169   return entry;
170 }
171
172
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)
180 {
181   if (entry->target)
182     MyFree(entry->target);
183
184   entry->next = freeList;
185   freeList = entry;
186 }
187
188
189 /*----------------------------------------------------------------------------
190  * ip_registry_update_free_targets:  
191  *--------------------------------------------------------------------------*/
192 static unsigned int ip_registry_update_free_targets(struct IPRegistryEntry  *entry)
193 {
194   unsigned int free_targets = STARTTARGETS;
195
196   if (entry->target) {
197     free_targets = (entry->target->count +
198                     (CONNECTED_SINCE(entry) / TARGET_DELAY));
199
200     if (free_targets > STARTTARGETS)
201       free_targets = STARTTARGETS;
202
203     entry->target->count = free_targets;
204   }
205
206   return free_targets;
207 }
208
209
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)
216 {
217   /*
218    * Don't touch this number, it has statistical significance
219    * XXX - blah blah blah
220    * ZS - Just -what- statistical significance does it -have-?
221    */
222   if (CONNECTED_SINCE(entry) > 600) {
223     ip_registry_remove(entry);
224     ip_registry_delete_entry(entry);
225   }
226   else if (CONNECTED_SINCE(entry) > 120 && 0 != entry->target) {
227     MyFree(entry->target);
228     entry->target = 0;
229   }
230 }
231
232
233 /*----------------------------------------------------------------------------
234  * ip_registry_expire:  Expire all of the needed entries in the hash table
235  *--------------------------------------------------------------------------*/
236 void ip_registry_expire(void)
237 {
238   struct IPRegistryEntry *entry;
239   struct IPRegistryEntry *entry_next;
240   static time_t   next_expire = 0;
241   int i;
242
243   /* Only do this if we're ready to */
244   if (next_expire >= CurrentTime)
245     return;
246
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);
252     }
253   }
254
255   next_expire = CurrentTime + 60;
256 }
257
258
259 /*----------------------------------------------------------------------------
260  * IPcheck_local_connect
261  *
262  * Event:
263  *   A new connection was accept()-ed with IP number `cptr->ip.s_addr'.
264  *
265  * Action:
266  *   Update the IPcheck registry.
267  *   Return:
268  *     1 : You're allowed to connect.
269  *     0 : You're not allowed to connect.
270  *
271  * Throttling:
272  *
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
276  * or less.
277  *
278  * Free target inheritance:
279  *
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)
287 {
288   struct IPRegistryEntry *entry        = ip_registry_find(addr);
289   unsigned int free_targets = STARTTARGETS;
290  
291   assert(0 != next_target_out);
292
293   if (0 == entry) {
294     entry = ip_registry_new_entry(addr, 1);
295     return 1;
296   }
297
298   /* Do not allow more than 255 connects from a single IP, EVER. */
299   if (0 == ++entry->connected)
300     return 0;
301
302   /* If our threshhold has elapsed, reset the counter so we don't throttle */
303   if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
304     entry->attempts = 0;
305   else if (0 == ++entry->attempts)
306     --entry->attempts;  /* Disallow overflow */
307
308   entry->last_connect = NOW;
309   free_targets = ip_registry_update_free_targets(entry);
310
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) {
314 #ifdef NOTHROTTLE 
315     return 1;
316 #else
317     --entry->connected;
318     return 0;
319 #endif        
320   }
321
322   return 1;
323 }
324
325
326 /*----------------------------------------------------------------------------
327  * ip_registry_local_connect
328  *
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)
333 {
334   assert(0 != cptr);
335   SetIPChecked(cptr);
336 }
337
338
339 /*----------------------------------------------------------------------------
340  * IPcheck_remote_connect
341  *
342  * Event:
343  *   A remote client connected to Undernet, with IP number `cptr->ip.s_addr'
344  *   and hostname `hostname'.
345  *
346  * Action:
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 {
352   struct IPRegistryEntry *entry;
353
354   assert(cptr);
355
356   entry = ip_registry_find(cptr->ip.s_addr);
357
358   SetIPChecked(cptr);
359
360   if (0 == entry)
361     entry = ip_registry_new_entry(cptr->ip.s_addr, (is_burst ? 0 : 1));
362   else {
363     /* NEVER more than 255 connections. */
364     if (0 == ++entry->connected)
365       return 0;
366
367     /* Make sure we don't bounce if our threshhold has expired */
368     if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
369       entry->attempts = 0;
370
371     /* If we're not part of a burst, go ahead and process the rest */
372     if (!is_burst) {
373       if (0 == ++entry->attempts)
374         --entry->attempts;  /* Overflows are bad, mmmkay? */
375       ip_registry_update_free_targets(entry);
376       entry->last_connect = NOW;
377     }
378   }
379
380   return 1;
381 }
382
383 /*----------------------------------------------------------------------------
384  * IPcheck_connect_fail
385  *
386  * Event:
387  *   This local client failed to connect due to legal reasons.
388  *
389  * Action:
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
392  *   again.
393  *--------------------------------------------------------------------------*/
394 void ip_registry_connect_fail(unsigned int addr)
395 {
396   struct IPRegistryEntry *entry = ip_registry_find(addr);
397
398   if (entry)
399     --entry->attempts;
400 }
401
402
403 /*----------------------------------------------------------------------------
404  * IPcheck_connect_succeeded
405  *
406  * Event:
407  *   A client succeeded to finish the registration.
408  *
409  * Finish IPcheck registration of a successfully, locally connected client.
410  *--------------------------------------------------------------------------*/
411 void ip_registry_connect_succeeded(struct Client *cptr)
412 {
413   const char     *tr           = "";
414   unsigned int free_targets     = STARTTARGETS;
415   struct IPRegistryEntry *entry;
416
417   assert(cptr);
418
419   entry = ip_registry_find(cptr->ip.s_addr);
420
421   if (!entry) {
422     Debug((DEBUG_ERROR, "Missing registry entry for: %s", cptr->sock_ip));
423     return;
424   }
425
426   if (entry->target) {
427     memcpy(cptr->targets, entry->target->targets, MAXTARGETS);
428     free_targets = entry->target->count;
429     tr = " tr";
430   }
431
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);
435 }
436
437
438 /*----------------------------------------------------------------------------
439  * IPcheck_disconnect
440  *
441  * Event:
442  *   A local client disconnected or a remote client left Undernet.
443  *
444  * Action:
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)
450 {
451   struct IPRegistryEntry *entry;
452
453   assert(0 != cptr);
454
455   if (!IsIPChecked(cptr))
456     return;
457
458   entry = ip_registry_find(cptr->ip.s_addr);
459
460   /* Entry is probably a server if this happens. */
461   if (0 == entry)
462     return;
463
464
465   /*
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.
469    */
470   if (0 == --entry->connected) {
471     if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD)
472       entry->attempts = 0;
473     ip_registry_update_free_targets(entry);
474     entry->last_connect = NOW;
475   }
476
477
478   if (MyConnect(cptr)) {
479     unsigned int free_targets;
480
481     if (0 == entry->target) {
482       entry->target = (struct IPTargetEntry *)MyMalloc(sizeof(struct IPTargetEntry));
483       assert(0 != entry->target);
484       entry->target->count = STARTTARGETS;
485     }
486     memcpy(entry->target->targets, cptr->targets, MAXTARGETS);
487
488     /*
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.
493      *
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.  
502          */
503     if (cptr->nexttarget < CurrentTime)
504       free_targets = (CurrentTime - cptr->nexttarget) / TARGET_DELAY + 1;
505     else
506       free_targets = 0;
507
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;
511
512     /* Finally, store smallest value for Judgement Day */
513     if (free_targets < entry->target->count)
514       entry->target->count = free_targets;
515   }
516 }
517
518 /*----------------------------------------------------------------------------
519  * IPcheck_nr
520  *
521  * Returns number of clients with the same IP number
522  *--------------------------------------------------------------------------*/
523 int ip_registry_count(unsigned int addr)
524 {
525   struct IPRegistryEntry *entry = ip_registry_find(addr);
526   return (entry) ? entry->connected : 0;
527 }
528
529
530
531
532