Author: ZenShadow
[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
23 /*----------------------------------------------------------------------------
24  * Platform Includes
25  *--------------------------------------------------------------------------*/
26 #include <assert.h>
27 #include <stdio.h>
28 #include <string.h>
29
30
31 /*----------------------------------------------------------------------------
32  * Application Includes
33  *--------------------------------------------------------------------------*/
34 #include "IPcheck.h"
35 #include "client.h"
36 #include "ircd.h"
37 #include "numnicks.h"
38 #include "ircd_alloc.h"
39 #include "msg.h"
40 #include "s_bsd.h"
41 #include "s_debug.h"
42 #include "s_user.h"
43 #include "send.h"
44
45
46 /*----------------------------------------------------------------------------
47  * Data Structures (should be moved to IPcheck.h)
48  *--------------------------------------------------------------------------*/
49 typedef struct IPTargetEntry {
50   int           count;
51   unsigned char targets[MAXTARGETS];
52 } iptarget_entry_t;
53
54 typedef struct IPRegistryEntry {
55   struct IPRegistryEntry *next;
56   struct IPTargetEntry   *target;
57   unsigned int             addr;
58   time_t                  last_connect;
59   unsigned char            connected;
60   unsigned char            attempts;
61 } ip_reg_entry_t;
62
63
64 /*
65  * Hash table for IPv4 address registry
66  *
67  * Hash table size must be a power of 2
68  * Use 64K hash table to conserve memory
69  */
70 /*----------------------------------------------------------------------------
71  * Compile-time Configuration
72  *--------------------------------------------------------------------------*/
73 #define IP_REGISTRY_TABLE_SIZE 0x10000
74 #define MASK_16                0xffff
75
76 #define IPCHECK_CLONE_LIMIT 2
77 #define IPCHECK_CLONE_PERIOD 20
78 #define IPCHECK_CLONE_DELAY  600
79
80
81 /*----------------------------------------------------------------------------
82  * Handy Macros
83  *--------------------------------------------------------------------------*/
84 #define NOW (CurrentTime)
85 #define CONNECTED_SINCE(x) (NOW - (x->last_connect))
86
87
88 /*----------------------------------------------------------------------------
89  * Global Data (ugly!)
90  *--------------------------------------------------------------------------*/
91 static ip_reg_entry_t *hashTable[IP_REGISTRY_TABLE_SIZE];
92 static ip_reg_entry_t *freeList = 0;
93
94
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);
103 }
104
105
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;
111
112   for (entry = hashTable[ip_registry_hash(ip)]; entry; entry = entry->next) {
113     if (entry->addr == ip)
114       break;
115   }
116
117   return entry;
118 }
119
120
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);
126
127   entry->next = hashTable[bucket];
128   hashTable[bucket] = entry;
129 }
130   
131
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);
137
138   if (hashTable[bucket] == entry)
139     hashTable[bucket] = entry->next;
140   else {
141     ip_reg_entry_t *prev;
142
143     for (prev = hashTable[bucket]; prev; prev = prev->next) {
144       if (prev->next == entry) {
145         prev->next = entry->next;
146         break;
147       }
148     }
149   }
150 }
151  
152
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;
159
160   if (entry)
161     freeList = entry->next;
162   else
163     entry = (ip_reg_entry_t *)MyMalloc(sizeof(ip_reg_entry_t));
164
165   assert(0 != entry);
166
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                 */
172
173   ip_registry_add(entry);
174
175   return entry;
176 }
177
178
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) {
186   if (entry->target)
187     MyFree(entry->target);
188
189   entry->next = freeList;
190   freeList = entry;
191 }
192
193
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;
199
200   if (entry->target) {
201     free_targets = (entry->target->count +
202                     (CONNECTED_SINCE(entry) / TARGET_DELAY));
203
204     if (free_targets > STARTTARGETS)
205       free_targets = STARTTARGETS;
206
207     entry->target->count = free_targets;
208   }
209
210   return free_targets;
211 }
212
213
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) {
220   /*
221    * Don't touch this number, it has statistical significance
222    * XXX - blah blah blah
223    * ZS - Just -what- statistical significance does it -have-?
224    */
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);
230     entry->target = 0;
231   }
232 }
233
234
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;
242   int i;
243
244   /* Only do this if we're ready to */
245   if (next_expire >= CurrentTime)
246     return;
247
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);
253     }
254   }
255
256   next_expire = CurrentTime + 60;
257 }
258
259
260 /*----------------------------------------------------------------------------
261  * IPcheck_local_connect
262  *
263  * Event:
264  *   A new connection was accept()-ed with IP number `cptr->ip.s_addr'.
265  *
266  * Action:
267  *   Update the IPcheck registry.
268  *   Return:
269  *     1 : You're allowed to connect.
270  *     0 : You're not allowed to connect.
271  *
272  * Throttling:
273  *
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
277  * or less.
278  *
279  * Free target inheritance:
280  *
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)
288 {
289   ip_reg_entry_t *entry        = ip_registry_find(addr);
290   unsigned int free_targets = STARTTARGETS;
291  
292   assert(0 != next_target_out);
293
294   if (0 == entry) {
295     entry = ip_registry_new_entry(addr, 1);
296     return 1;
297   }
298
299   /* Do not allow more than 255 connects from a single IP, EVER. */
300   if (0 == ++entry->connected)
301     return 0;
302
303   /* If our threshhold has elapsed, reset the counter so we don't throttle */
304   if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
305     entry->attempts = 0;
306   else if (0 == ++entry->attempts)
307     --entry->attempts;  /* Disallow overflow */
308
309   entry->last_connect = NOW;
310   free_targets = ip_registry_update_free_targets(entry);
311
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) {
315 #ifdef NOTHROTTLE 
316     return 1;
317 #else
318     --entry->connected;
319     return 0;
320 #endif        
321   }
322
323   return 1;
324 }
325
326
327 /*----------------------------------------------------------------------------
328  * ip_registry_local_connect
329  *
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) {
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   ip_reg_entry_t *entry;
352
353   assert(cptr);
354
355   entry = ip_registry_find(cptr->ip.s_addr);
356
357   SetIPChecked(cptr);
358
359   if (0 == entry)
360     entry = ip_registry_new_entry(cptr->ip.s_addr, (is_burst ? 0 : 1));
361   else {
362     /* NEVER more than 255 connections. */
363     if (0 == ++entry->connected)
364       return 0;
365
366     /* Make sure we don't bounce if our threshhold has expired */
367     if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
368       entry->attempts = 0;
369
370     /* If we're not part of a burst, go ahead and process the rest */
371     if (!is_burst) {
372       if (0 == ++entry->attempts)
373         --entry->attempts;  /* Overflows are bad, mmmkay? */
374       ip_registry_update_free_targets(entry);
375       entry->last_connect = NOW;
376     }
377   }
378
379   return 1;
380 }
381
382 /*----------------------------------------------------------------------------
383  * IPcheck_connect_fail
384  *
385  * Event:
386  *   This local client failed to connect due to legal reasons.
387  *
388  * Action:
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
391  *   again.
392  *--------------------------------------------------------------------------*/
393 void ip_registry_connect_fail(unsigned int addr) {
394   ip_reg_entry_t *entry = ip_registry_find(addr);
395
396   if (entry)
397     --entry->attempts;
398 }
399
400
401 /*----------------------------------------------------------------------------
402  * IPcheck_connect_succeeded
403  *
404  * Event:
405  *   A client succeeded to finish the registration.
406  *
407  * Finish IPcheck registration of a successfully, locally connected client.
408  *--------------------------------------------------------------------------*/
409 void ip_registry_connect_succeeded(struct Client *cptr) {
410   const char     *tr           = "";
411   unsigned int free_targets     = STARTTARGETS;
412   ip_reg_entry_t *entry;
413
414   assert(cptr);
415
416   entry = ip_registry_find(cptr->ip.s_addr);
417
418   if (!entry) {
419     Debug((DEBUG_ERROR, "Missing registry entry for: %s", cptr->sock_ip));
420     return;
421   }
422
423   if (entry->target) {
424     memcpy(cptr->targets, entry->target->targets, MAXTARGETS);
425     free_targets = entry->target->count;
426     tr = " tr";
427   }
428
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);
432 }
433
434
435 /*----------------------------------------------------------------------------
436  * IPcheck_disconnect
437  *
438  * Event:
439  *   A local client disconnected or a remote client left Undernet.
440  *
441  * Action:
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;
448
449   assert(0 != cptr);
450
451   if (!IsIPChecked(cptr))
452     return;
453
454   entry = ip_registry_find(cptr->ip.s_addr);
455
456   /* Entry is probably a server if this happens. */
457   if (0 == entry)
458     return;
459
460
461   /*
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.
465    */
466   if (0 == --entry->connected) {
467     if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD)
468       entry->attempts = 0;
469     ip_registry_update_free_targets(entry);
470     entry->last_connect = NOW;
471   }
472
473
474   if (MyConnect(cptr)) {
475     unsigned int free_targets;
476
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;
481     }
482     memcpy(entry->target->targets, cptr->targets, MAXTARGETS);
483
484     /*
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.
489      *
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.  
498          */
499     if (cptr->nexttarget < CurrentTime)
500       free_targets = (CurrentTime - cptr->nexttarget) / TARGET_DELAY + 1;
501     else
502       free_targets = 0;
503
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;
507
508     /* Finally, store smallest value for Judgement Day */
509     if (free_targets < entry->target->count)
510       entry->target->count = free_targets;
511   }
512 }
513
514 /*----------------------------------------------------------------------------
515  * IPcheck_nr
516  *
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;
522 }
523
524
525
526
527