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  * This file should be edited in a window with a width of 141 characters
23  * ick
24  */
25 #include "IPcheck.h"
26 #include "client.h"
27 #include "ircd.h"
28 #include "numnicks.h"       /* NumNick, NumServ (GODMODE) */
29 #include "ircd_alloc.h"
30 #include "s_bsd.h"          /* SetIPChecked */
31 #include "s_debug.h"        /* Debug */
32 #include "s_user.h"         /* TARGET_DELAY */
33 #include "send.h"
34
35 #include <assert.h>
36 #include <stdio.h>          /* NULL ... bleah */
37
38
39 struct IPTargetEntry {
40   int           count;
41   unsigned char targets[MAXTARGETS];
42 };
43
44 struct IPRegistryEntry {
45   struct IPRegistryEntry*  next;
46   struct IPTargetEntry*    target;
47   unsigned int             addr;
48   unsigned short           last_connect;
49   unsigned char            connected;
50   unsigned char            attempts;
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 #define IP_REGISTRY_TABLE_SIZE 0x10000
60 #define MASK_16                0xffff
61
62 #define NOW ((unsigned short)(CurrentTime & MASK_16))
63 #define CONNECTED_SINCE(x) (NOW - (x))
64
65 #define IPCHECK_CLONE_LIMIT 2
66 #define IPCHECK_CLONE_PERIOD 20
67 #define IPCHECK_CLONE_DELAY 600
68
69
70 static struct IPRegistryEntry* hashTable[IP_REGISTRY_TABLE_SIZE];
71 static struct IPRegistryEntry* freeList = 0;
72
73 static unsigned int ip_registry_hash(unsigned int ip)
74 {
75   return ((ip >> 16) ^ ip) & (IP_REGISTRY_TABLE_SIZE - 1);
76 }
77
78 static struct IPRegistryEntry* ip_registry_find(unsigned int ip)
79 {
80   struct IPRegistryEntry* entry = hashTable[ip_registry_hash(ip)];
81   for ( ; entry; entry = entry->next) {
82     if (entry->addr == ip)
83       break;
84   }
85   return entry;
86 }
87
88 static void ip_registry_add(struct IPRegistryEntry* entry)
89 {
90   unsigned int bucket = ip_registry_hash(entry->addr);
91   entry->next = hashTable[bucket];
92   hashTable[bucket] = entry;
93 }
94   
95 static void ip_registry_remove(struct IPRegistryEntry* entry)
96 {
97   unsigned int bucket = ip_registry_hash(entry->addr);
98   if (hashTable[bucket] == entry)
99     hashTable[bucket] = entry->next;
100   else {
101     struct IPRegistryEntry* prev = hashTable[bucket];
102     for ( ; prev; prev = prev->next) {
103       if (prev->next == entry) {
104         prev->next = entry->next;
105         break;
106       }
107     }
108   }
109 }
110  
111 static struct IPRegistryEntry* ip_registry_new_entry()
112 {
113   struct IPRegistryEntry* entry = freeList;
114   if (entry)
115     freeList = entry->next;
116   else
117     entry = (struct IPRegistryEntry*) MyMalloc(sizeof(struct IPRegistryEntry));
118
119   assert(0 != entry);
120   memset(entry, 0, sizeof(struct IPRegistryEntry));
121   entry->last_connect = NOW;     /* Seconds since last connect attempt */
122   entry->connected    = 1;       /* connected clients for this IP */
123   entry->attempts     = 1;       /* Number attempts for this IP */
124   return entry;
125 }
126
127 static void ip_registry_delete_entry(struct IPRegistryEntry* entry)
128 {
129   if (entry->target)
130     MyFree(entry->target);
131   entry->next = freeList;
132   freeList = entry;
133 }
134
135 static unsigned int ip_registry_update_free_targets(struct IPRegistryEntry* entry)
136 {
137   unsigned int free_targets = STARTTARGETS;
138
139   if (entry->target) {
140     free_targets = entry->target->count + (CONNECTED_SINCE(entry->last_connect) / TARGET_DELAY);
141     if (free_targets > STARTTARGETS)
142       free_targets = STARTTARGETS;
143     entry->target->count = free_targets;
144   }
145   return free_targets;
146 }
147
148 static void ip_registry_expire_entry(struct IPRegistryEntry* entry)
149 {
150   /*
151    * Don't touch this number, it has statistical significance
152    * XXX - blah blah blah
153    */
154   if (CONNECTED_SINCE(entry->last_connect) > 600) {
155     /*
156      * expired
157      */
158     ip_registry_remove(entry);
159     ip_registry_delete_entry(entry);
160   }
161   else if (CONNECTED_SINCE(entry->last_connect) > 120 && 0 != entry->target) {
162     /*
163      * Expire storage of targets
164      */
165     MyFree(entry->target);
166     entry->target = 0;
167   }
168 }
169
170 /*
171  * ip_registry_expire
172  */
173 static void ip_registry_expire()
174 {
175   int i;
176   struct IPRegistryEntry* entry;
177   struct IPRegistryEntry* entry_next;
178
179   for (i = 0; i < IP_REGISTRY_TABLE_SIZE; ++i) {
180     for (entry = hashTable[i]; entry; entry = entry_next) {
181       entry_next = entry->next;
182       if (0 == entry->connected)
183         ip_registry_expire_entry(entry);
184     }
185   }
186 }
187
188 /*
189  * IPcheck_local_connect
190  *
191  * Event:
192  *   A new connection was accept()-ed with IP number `cptr->ip.s_addr'.
193  *
194  * Action:
195  *   Update the IPcheck registry.
196  *   Return:
197  *     1 : You're allowed to connect.
198  *     0 : You're not allowed to connect.
199  *
200  * Throttling:
201  *
202  * A connection should be rejected when a connection from the same IP number was
203  * received IPCHECK_CLONE_LIMIT times before this connect attempt, with
204  * reconnect intervals of IPCHECK_CLONE_PERIOD seconds or less.
205  *
206  * Free target inheritance:
207  *
208  * When the client is accepted, then the number of Free Targets
209  * of the cptr is set to the value stored in the found IPregistry
210  * structure, or left at STARTTARGETS.  This can be done by changing
211  * cptr->nexttarget to be `now - (TARGET_DELAY * (FREE_TARGETS - 1))',
212  * where FREE_TARGETS may range from 0 till STARTTARGETS.
213  */
214 int ip_registry_check_local(unsigned int addr, time_t* next_target_out)
215 {
216   struct IPRegistryEntry* entry = ip_registry_find(addr);
217   unsigned int free_targets = STARTTARGETS;
218  
219   if (0 == entry) {
220     entry       = ip_registry_new_entry();
221     entry->addr = addr;    /* The IP number of registry entry */
222     ip_registry_add(entry);
223     return 1;
224   }
225   /* Note that this also connects server connects.
226    * It is hard and not interesting, to change that.
227    *
228    * Don't allow more then 255 connects from one IP number, ever
229    */
230   if (0 == ++entry->connected)
231     return 0;
232
233   if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_PERIOD)
234     entry->attempts = 0;
235
236   free_targets = ip_registry_update_free_targets(entry);
237   entry->last_connect = NOW;
238
239   if (0 == ++entry->attempts)   /* Check for overflow */
240     --entry->attempts;
241
242   if (entry->attempts < IPCHECK_CLONE_LIMIT) {
243     if (next_target_out)
244       *next_target_out = CurrentTime - (TARGET_DELAY * free_targets - 1);
245   }
246   else if ((CurrentTime - me.since) > IPCHECK_CLONE_DELAY) {
247     /* 
248      * Don't refuse connection when we just rebooted the server
249      */
250 #ifdef NOTHROTTLE 
251     return 1;
252 #else
253     return 0;
254 #endif        
255   }
256   return 1;
257 }
258
259 /*
260  * IPcheck_remote_connect
261  *
262  * Event:
263  *   A remote client connected to Undernet, with IP number `cptr->ip.s_addr'
264  *   and hostname `hostname'.
265  *
266  * Action:
267  *   Update the IPcheck registry.
268  *   Return 0 on failure, 1 on success.
269  */
270 int ip_registry_check_remote(struct Client* cptr, int is_burst)
271 {
272   struct IPRegistryEntry* entry = ip_registry_find(cptr->ip.s_addr);
273
274   /*
275    * Mark that we did add/update an IPregistry entry
276    */
277   SetIPChecked(cptr);
278   if (0 == entry) {
279     entry = ip_registry_new_entry();
280     entry->addr = cptr->ip.s_addr;
281     if (is_burst)
282       entry->attempts = 0;
283     ip_registry_add(entry);
284   }
285   else {
286     if (0 == ++entry->connected) {
287       /* 
288        * Don't allow more then 255 connects from one IP number, ever
289        */
290       return 0;
291     }
292     if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_PERIOD)
293       entry->attempts = 0;
294     if (!is_burst) {
295       if (0 == ++entry->attempts) {
296         /*
297          * Check for overflow
298          */
299         --entry->attempts;
300       }
301       ip_registry_update_free_targets(entry);
302       entry->last_connect = NOW;
303     }
304   }
305   return 1;
306 }
307
308 /*
309  * IPcheck_connect_fail
310  *
311  * Event:
312  *   This local client failed to connect due to legal reasons.
313  *
314  * Action:
315  *   Neutralize the effect of calling IPcheck_local_connect, in such
316  *   a way that the client won't be penalized when trying to reconnect
317  *   again.
318  */
319 void ip_registry_connect_fail(unsigned int addr)
320 {
321   struct IPRegistryEntry* entry = ip_registry_find(addr);
322   if (entry)
323     --entry->attempts;
324 }
325
326 /*
327  * IPcheck_connect_succeeded
328  *
329  * Event:
330  *   A client succeeded to finish the registration.
331  *
332  * Finish IPcheck registration of a successfully, locally connected client.
333  */
334 void ip_registry_connect_succeeded(struct Client *cptr)
335 {
336   const char*             tr    = "";
337   unsigned int free_targets     = STARTTARGETS;
338   struct IPRegistryEntry* entry = ip_registry_find(cptr->ip.s_addr);
339
340   if (!entry) {
341     Debug((DEBUG_ERROR, "Missing registry entry for: %s", cptr->sock_ip));
342     return;
343   }
344   if (entry->target) {
345     memcpy(cptr->targets, entry->target->targets, MAXTARGETS);
346     free_targets = entry->target->count;
347     tr = " tr";
348   }
349   sendto_one(cptr, ":%s NOTICE %s :on %u ca %u(%u) ft %u(%u)%s",
350              me.name, cptr->name, entry->connected, entry->attempts,
351              IPCHECK_CLONE_LIMIT, free_targets, STARTTARGETS, tr);
352 }
353
354 /*
355  * IPcheck_disconnect
356  *
357  * Event:
358  *   A local client disconnected or a remote client left Undernet.
359  *
360  * Action:
361  *   Update the IPcheck registry.
362  *   Remove all expired IPregistry structures from the hash bucket
363  *     that belongs to this clients IP number.
364  */
365 void ip_registry_disconnect(struct Client *cptr)
366 {
367   struct IPRegistryEntry* entry = ip_registry_find(cptr->ip.s_addr);
368   if (0 == entry) {
369     /*
370      * trying to find an entry for a server causes this to happen,
371      * servers should never have FLAGS_IPCHECK set
372      */
373     return;
374   }
375   /*
376    * If this was the last one, set `last_connect' to disconnect time (used for expiration)
377    */
378   if (0 == --entry->connected) {
379     if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD) {
380       /*
381        * Otherwise we'd penetalize for this old value if the client reconnects within 20 seconds
382        */
383       entry->attempts = 0;
384     }
385     ip_registry_update_free_targets(entry);
386     entry->last_connect = NOW;
387   }
388   if (MyConnect(cptr)) {
389     unsigned int free_targets;
390     /*
391      * Copy the clients targets
392      */
393     if (0 == entry->target) {
394       entry->target = (struct IPTargetEntry*) MyMalloc(sizeof(struct IPTargetEntry));
395       entry->target->count = STARTTARGETS;
396     }
397     assert(0 != entry->target);
398
399     memcpy(entry->target->targets, cptr->targets, MAXTARGETS);
400     /*
401      * This calculation can be pretty unfair towards large multi-user hosts, but
402      * there is "nothing" we can do without also allowing spam bots to send more
403      * messages or by drastically increasing the ammount of memory used in the IPregistry.
404      *
405      * The problem is that when a client disconnects, leaving no free targets, then
406      * the next client from that IP number has to pay for it (getting no free targets).
407      * But ALSO the next client, and the next client, and the next client etc - until
408      * another client disconnects that DOES leave free targets.  The reason for this
409      * is that if there are 10 SPAM bots, and they all disconnect at once, then they
410      * ALL should get no free targets when reconnecting.  We'd need to store an entry
411      * per client (instead of per IP number) to avoid this.
412      */
413     if (cptr->nexttarget < CurrentTime) {
414         /*
415          * Number of free targets
416          */
417       free_targets = (CurrentTime - cptr->nexttarget) / TARGET_DELAY + 1;
418     }
419     else
420       free_targets = 0;
421     /*
422      * Add bonus, this is pretty fuzzy, but it will help in some cases.
423      */
424     if ((CurrentTime - cptr->firsttime) > 600)
425       /*
426        * Was longer then 10 minutes online?
427        */
428       free_targets += (CurrentTime - cptr->firsttime - 600) / TARGET_DELAY;
429     /*
430      * Finally, store smallest value for Judgement Day
431      */
432     if (free_targets < entry->target->count)
433       entry->target->count = free_targets;
434   }
435 }
436
437 /*
438  * IPcheck_nr
439  *
440  * Returns number of clients with the same IP number
441  */
442 int ip_registry_count(unsigned int addr)
443 {
444   struct IPRegistryEntry* entry = ip_registry_find(addr);
445   return (entry) ? entry->connected : 0;
446 }
447
448 /*
449  * IPcheck_local_connect
450  *
451  * Event:
452  *   A new connection was accept()-ed with IP number `cptr->ip.s_addr'.
453  *
454  * Action:
455  *   Update the IPcheck registry.
456  *   Return:
457  *     1 : You're allowed to connect.
458  *     0 : You're not allowed to connect.
459  *
460  * Throttling:
461  *
462  * A connection should be rejected when a connection from the same IP number was
463  * received IPCHECK_CLONE_LIMIT times before this connect attempt, with
464  * reconnect intervals of IPCHECK_CLONE_PERIOD seconds or less.
465  *
466  * Free target inheritance:
467  *
468  * When the client is accepted, then the number of Free Targets
469  * of the cptr is set to the value stored in the found IPregistry
470  * structure, or left at STARTTARGETS.  This can be done by changing
471  * cptr->nexttarget to be `now - (TARGET_DELAY * (FREE_TARGETS - 1))',
472  * where FREE_TARGETS may range from 0 till STARTTARGETS.
473  */
474 int IPcheck_local_connect(struct in_addr a, time_t* next_target_out)
475 {
476   assert(0 != next_target_out);
477   return ip_registry_check_local(a.s_addr, next_target_out);
478 }
479
480 /*
481  * IPcheck_remote_connect
482  *
483  * Event:
484  *   A remote client connected to Undernet, with IP number `cptr->ip.s_addr'
485  *   and hostname `hostname'.
486  *
487  * Action:
488  *   Update the IPcheck registry.
489  *   Return 0 on failure, 1 on success.
490  */
491 int IPcheck_remote_connect(struct Client *cptr, const char *hostname, int is_burst)
492 {
493   assert(0 != cptr);
494   return ip_registry_check_remote(cptr, is_burst);
495 }
496
497 /*
498  * IPcheck_connect_fail
499  *
500  * Event:
501  *   This local client failed to connect due to legal reasons.
502  *
503  * Action:
504  *   Neutralize the effect of calling IPcheck_local_connect, in such
505  *   a way that the client won't be penalized when trying to reconnect
506  *   again.
507  */
508 void IPcheck_connect_fail(struct in_addr a)
509 {
510   ip_registry_connect_fail(a.s_addr);
511 }
512
513 /*
514  * IPcheck_connect_succeeded
515  *
516  * Event:
517  *   A client succeeded to finish the registration.
518  *
519  * Finish IPcheck registration of a successfully, locally connected client.
520  */
521 void IPcheck_connect_succeeded(struct Client *cptr)
522 {
523   assert(0 != cptr);
524   ip_registry_connect_succeeded(cptr);
525 }
526
527 /*
528  * IPcheck_disconnect
529  *
530  * Event:
531  *   A local client disconnected or a remote client left Undernet.
532  *
533  * Action:
534  *   Update the IPcheck registry.
535  *   Remove all expired IPregistry structures from the hash bucket
536  *     that belongs to this clients IP number.
537  */
538 void IPcheck_disconnect(struct Client *cptr)
539 {
540   assert(0 != cptr);
541   ip_registry_disconnect(cptr);
542 }
543
544 /*
545  * IPcheck_nr
546  *
547  * Returns number of clients with the same IP number
548  */
549 unsigned short IPcheck_nr(struct Client *cptr)
550 {
551   assert(0 != cptr);
552   return ip_registry_count(cptr->ip.s_addr);
553 }
554
555 void IPcheck_expire()
556 {
557   static time_t next_expire = 0;
558   if (next_expire < CurrentTime) {
559     ip_registry_expire();
560     next_expire = CurrentTime + 60;
561   }
562 }
563
564 #if 0
565 struct IPregistry_vector {
566   unsigned short length;
567   unsigned short allocated_length;
568   struct IPregistry *vector;
569 };
570
571 #define HASHTABSIZE 0x2000      /* Must be power of 2 */
572 static struct IPregistry_vector IPregistry_hashtable[HASHTABSIZE];
573
574 /*
575  * Calculate a `hash' value between 0 and HASHTABSIZE, from the internet address `in_addr'.
576  * Apply it immedeately to the table, effectively hiding the table itself.
577  */
578 #define CALCULATE_HASH(in_addr) \
579   struct IPregistry_vector *hash; \
580   do { unsigned int ip = (in_addr).s_addr; \
581        hash = &IPregistry_hashtable[((ip >> 14) + (ip >> 7) + ip) & (HASHTABSIZE - 1)]; } while(0)
582
583 /*
584  * Fit `now' in an unsigned short, the advantage is that we use less memory
585  * `struct IPregistry::last_connect' can be smaller while the only disadvantage 
586  * is that if someone reconnects after exactly 18 hours and 12 minutes, and NOBODY with the
587  * same _hash_ value for this IP-number did disconnect in the meantime, then the server
588  * will think he reconnected immedeately. In other words: No disadvantage at all.
589  */
590 #define BITMASK 0xffff          /* Same number of bits as `struct IPregistry::last_connect' */
591 #define HAS_TARGETS_MAGIC 15
592 #define HAS_TARGETS(entry) ((entry)->free_targets == HAS_TARGETS_MAGIC)
593
594 #if STARTTARGETS >= HAS_TARGETS_MAGIC
595 #error "That doesn't fit in 4 bits, does it?"
596 #endif
597
598 /* IP(entry) returns the `struct in_addr' of the IPregistry. */
599 #define IP(entry) (HAS_TARGETS(entry) ? (entry)->ip_targets.ptr->ip : (entry)->ip_targets.ip)
600 #define FREE_TARGETS(entry) (HAS_TARGETS(entry) ? (entry)->ip_targets.ptr->free_targets : (entry)->free_targets)
601
602 static unsigned short count = 10000, average_length = 4;
603
604 static struct IPregistry *IPregistry_add(struct IPregistry_vector *iprv)
605 {
606   assert(0 != iprv);
607   if (iprv->length == iprv->allocated_length)
608   {
609     iprv->allocated_length += 4;
610     if (iprv->vector) {
611       iprv->vector = 
612               (struct IPregistry*) MyRealloc(iprv->vector,
613                        iprv->allocated_length * sizeof(struct IPregistry));
614     }
615     else {
616       iprv->vector = 
617               (struct IPregistry*) MyMalloc(
618                        iprv->allocated_length * sizeof(struct IPregistry));
619     }
620   }
621   return &iprv->vector[iprv->length++];
622 }
623
624 static struct IPregistry *IPregistry_find(struct IPregistry_vector *iprv,
625     struct in_addr ip)
626 {
627   if (iprv->length > 0)
628   {
629     struct IPregistry *i, *end = &iprv->vector[iprv->length];
630     for (i = &iprv->vector[0]; i < end; ++i)
631       if (IP(i).s_addr == ip.s_addr)
632         return i;
633   }
634   return NULL;
635 }
636
637 static struct IPregistry *IPregistry_find_with_expire(struct IPregistry_vector
638     *iprv, struct in_addr ip)
639 {
640   struct IPregistry *last;
641   struct IPregistry *curr;
642   struct IPregistry *retval = NULL;
643
644   /*
645    * if the vector is empty, IPcheck_disconnect will cause the server
646    * to core when NDEBUG is defined
647    */
648   if (iprv->length < 1)
649     return retval;
650
651   last = &iprv->vector[iprv->length - 1];
652
653   for (curr = &iprv->vector[0]; curr < last;)
654   {
655     if (IP(curr).s_addr == ip.s_addr)
656       /* `curr' is element we looked for */
657       retval = curr;
658     else if (curr->connected == 0)
659     {
660       if (CONNECTED_SINCE(curr) > 600U) /* Don't touch this number, it has statistical significance */
661       {
662         /* `curr' expired */
663         if (HAS_TARGETS(curr))
664           MyFree(curr->ip_targets.ptr);
665         *curr = *last--;
666         iprv->length--;
667         if (--count == 0)
668         {
669           /* Make ever 10000 disconnects an estimation of the average vector length */
670           count = 10000;
671           average_length =
672               (UserStats.clients + UserStats.unknowns + UserStats.local_servers) / HASHTABSIZE;
673         }
674         /* Now check the new element (last) that was moved to this position */
675         continue;
676       }
677       else if (CONNECTED_SINCE(curr) > 120U && HAS_TARGETS(curr))
678       {
679         /* Expire storage of targets */
680         struct in_addr ip1 = curr->ip_targets.ptr->ip;
681         curr->free_targets = curr->ip_targets.ptr->free_targets;
682         MyFree(curr->ip_targets.ptr);
683         curr->ip_targets.ip = ip1;
684       }
685     }
686     /* Did not expire, check next element */
687     ++curr;
688   }
689   /* Now check the last element in the list (curr == last) */
690   if (IP(curr).s_addr == ip.s_addr)
691     /* `curr' is element we looked for */
692     retval = curr;
693   else if (curr->connected == 0)
694   {
695     if (CONNECTED_SINCE(curr) > 600U)   /* Don't touch this number, it has statistical significance */
696     {
697       /* `curr' expired */
698       if (HAS_TARGETS(curr))
699         MyFree(curr->ip_targets.ptr);
700       iprv->length--;
701       if (--count == 0)
702       {
703         /* Make ever 10000 disconnects an estimation of the average vector length */
704         count = 10000;
705         average_length =
706             (UserStats.clients + UserStats.unknowns + UserStats.local_servers) / HASHTABSIZE;
707       }
708     }
709     else if (CONNECTED_SINCE(curr) > 120U && HAS_TARGETS(curr))
710     {
711       /* Expire storage of targets */
712       struct in_addr ip1 = curr->ip_targets.ptr->ip;
713       curr->free_targets = curr->ip_targets.ptr->free_targets;
714       MyFree(curr->ip_targets.ptr);
715       curr->ip_targets.ip = ip1;
716     }
717   }
718   /* Do we need to shrink the vector? */
719   if (iprv->allocated_length > average_length
720       && iprv->allocated_length - iprv->length >= 4)
721   {
722     struct IPregistry *newpos;
723     iprv->allocated_length = iprv->length;
724     newpos =
725         (struct IPregistry *)MyRealloc(iprv->vector,
726         iprv->allocated_length * sizeof(struct IPregistry));
727     if (newpos != iprv->vector) /* Is this ever true? */
728     {
729       retval =
730           (struct IPregistry *)((char *)retval + ((char *)newpos -
731           (char *)iprv->vector));
732       iprv->vector = newpos;
733     }
734   }
735   return retval;
736 }
737
738 static void reset_connect_time(struct IPregistry *entry)
739 {
740   unsigned int previous_free_targets;
741
742   /* Apply aging */
743   previous_free_targets =
744       FREE_TARGETS(entry) + CONNECTED_SINCE(entry) / TARGET_DELAY;
745   if (previous_free_targets > STARTTARGETS)
746     previous_free_targets = STARTTARGETS;
747   if (HAS_TARGETS(entry))
748     entry->ip_targets.ptr->free_targets = previous_free_targets;
749   else
750     entry->free_targets = previous_free_targets;
751
752   entry->last_connect = NOW;
753 }
754
755 /*
756  * IPcheck_local_connect
757  *
758  * Event:
759  *   A new connection was accept()-ed with IP number `cptr->ip.s_addr'.
760  *
761  * Action:
762  *   Update the IPcheck registry.
763  *   Return:
764  *     1 : You're allowed to connect.
765  *     0 : You're not allowed to connect.
766  *
767  * Throttling:
768  *
769  * A connection should be rejected when a connection from the same IP number was
770  * received IPCHECK_CLONE_LIMIT times before this connect attempt, with
771  * reconnect intervals of IPCHECK_CLONE_PERIOD seconds or less.
772  *
773  * Free target inheritance:
774  *
775  * When the client is accepted, then the number of Free Targets
776  * of the cptr is set to the value stored in the found IPregistry
777  * structure, or left at STARTTARGETS.  This can be done by changing
778  * cptr->nexttarget to be `now - (TARGET_DELAY * (FREE_TARGETS - 1))',
779  * where FREE_TARGETS may range from 0 till STARTTARGETS.
780  */
781 int IPcheck_local_connect(struct in_addr a, time_t* next_target_out)
782 {
783   struct IPregistry *entry;
784   CALCULATE_HASH(a);
785   assert(0 != next_target_out);
786
787   if (!(entry = IPregistry_find(hash, a)))
788   {
789     entry = IPregistry_add(hash);
790     entry->ip_targets.ip = a;          /* The IP number of registry entry */
791     entry->last_connect = NOW;          /* Seconds since last connect attempt */
792     entry->connected = 1;               /* connected clients for this IP */
793     entry->connect_attempts = 1;        /* Number attempts for this IP */
794     entry->free_targets = STARTTARGETS; /* free targets a client gets */
795     return 1;
796   }
797   /* Note that this also connects server connects.
798    * It is hard and not interesting, to change that.
799    *
800    * Don't allow more then 255 connects from one IP number, ever
801    */
802   if (0 == ++entry->connected)
803     return 0;
804
805   if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
806     entry->connect_attempts = 0;
807
808   reset_connect_time(entry);
809
810   if (0 == ++entry->connect_attempts)   /* Check for overflow */
811     --entry->connect_attempts;
812
813   if (entry->connect_attempts <= IPCHECK_CLONE_LIMIT)
814     *next_target_out = CurrentTime - (TARGET_DELAY * (FREE_TARGETS(entry) - 1));
815
816   /* Don't refuse connection when we just rebooted the server */
817   else if (CurrentTime - me.since > IPCHECK_CLONE_DELAY)
818 #ifndef NOTHROTTLE 
819     return 0;
820 #else
821     return 1;
822 #endif        
823   return 1;
824 }
825
826 /*
827  * IPcheck_remote_connect
828  *
829  * Event:
830  *   A remote client connected to Undernet, with IP number `cptr->ip.s_addr'
831  *   and hostname `hostname'.
832  *
833  * Action:
834  *   Update the IPcheck registry.
835  *   Return -1 on failure, 0 on success.
836  */
837 int IPcheck_remote_connect(struct Client *cptr, const char *hostname,
838     int is_burst)
839 {
840   struct IPregistry *entry;
841   CALCULATE_HASH(cptr->ip);
842   SetIPChecked(cptr);           /* Mark that we did add/update an IPregistry entry */
843   if (!(entry = IPregistry_find(hash, cptr->ip)))
844   {
845     entry = IPregistry_add(hash);
846     entry->ip_targets.ip = cptr->ip;    /* The IP number of registry entry */
847     entry->last_connect = NOW;  /* Seconds since last connect (attempt) */
848     entry->connected = 1;       /* Number of currently connected clients with this IP number */
849     entry->connect_attempts = is_burst ? 1 : 0; /* Number of clients that connected with this IP number */
850     entry->free_targets = STARTTARGETS; /* Number of free targets that a client gets on connect */
851   }
852   else
853   {
854 #ifdef GODMODE
855     sendto_one(cptr,
856         "%s NOTICE %s%s :I saw your face before my friend (connected: %u; connect_attempts %u; free_targets %u)",
857         NumServ(&me), NumNick(cptr), entry->connected, entry->connect_attempts,
858         FREE_TARGETS(entry));
859 #endif
860     if (++(entry->connected) == 0)      /* Don't allow more then 255 connects from one IP number, ever */
861       return -1;
862     if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD)
863       entry->connect_attempts = 0;
864     if (!is_burst)
865     {
866       if (++(entry->connect_attempts) == 0)     /* Check for overflow */
867         --(entry->connect_attempts);
868       reset_connect_time(entry);
869     }
870   }
871   return 0;
872 }
873
874 /*
875  * IPcheck_connect_fail
876  *
877  * Event:
878  *   This local client failed to connect due to legal reasons.
879  *
880  * Action:
881  *   Neutralize the effect of calling IPcheck_local_connect, in such
882  *   a way that the client won't be penalized when trying to reconnect
883  *   again.
884  */
885 void IPcheck_connect_fail(struct in_addr a)
886 {
887   struct IPregistry *entry;
888   CALCULATE_HASH(a);
889   if ((entry = IPregistry_find(hash, a)))
890     --entry->connect_attempts;
891 }
892
893 /*
894  * IPcheck_connect_succeeded
895  *
896  * Event:
897  *   A client succeeded to finish the registration.
898  *
899  * Finish IPcheck registration of a successfully, locally connected client.
900  */
901 void IPcheck_connect_succeeded(struct Client *cptr)
902 {
903   struct IPregistry *entry;
904   const char *tr = "";
905   CALCULATE_HASH(cptr->ip);
906   entry = IPregistry_find(hash, cptr->ip);
907   if (HAS_TARGETS(entry))
908   {
909     memcpy(cptr->targets, entry->ip_targets.ptr->targets, MAXTARGETS);
910     tr = " tr";
911   }
912   sendto_one(cptr, ":%s NOTICE %s :on %u ca %u(%u) ft %u(%u)%s",
913       me.name, cptr->name, entry->connected, entry->connect_attempts,
914       IPCHECK_CLONE_LIMIT, FREE_TARGETS(entry), STARTTARGETS, tr);
915 }
916
917 /*
918  * IPcheck_disconnect
919  *
920  * Event:
921  *   A local client disconnected or a remote client left Undernet.
922  *
923  * Action:
924  *   Update the IPcheck registry.
925  *   Remove all expired IPregistry structures from the hash bucket
926  *     that belongs to this clients IP number.
927  */
928 void IPcheck_disconnect(struct Client *cptr)
929 {
930   struct IPregistry *entry;
931   CALCULATE_HASH(cptr->ip);
932   entry = IPregistry_find_with_expire(hash, cptr->ip);
933   if (0 == entry) {
934     /*
935      * trying to find an entry for a server causes this to happen,
936      * servers should never have FLAGS_IPCHECK set
937      */
938     assert(0 != entry);
939     return;
940   }
941   /*
942    * If this was the last one, set `last_connect' to disconnect time (used for expiration)
943    */
944   if (--(entry->connected) == 0) {
945     if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD)
946       /*
947        * Otherwise we'd penetalize for this old value if the client reconnects within 20 seconds
948        */
949       entry->connect_attempts = 0;
950     reset_connect_time(entry);
951   }
952   if (MyConnect(cptr)) {
953     unsigned int inheritance;
954     /*
955      * Copy the clients targets
956      */
957     if (HAS_TARGETS(entry)) {
958       entry->free_targets = entry->ip_targets.ptr->free_targets;
959       MyFree(entry->ip_targets.ptr);
960     }
961     entry->ip_targets.ptr =
962         (struct ip_targets_st*) MyMalloc(sizeof(struct ip_targets_st));
963
964     assert(0 != entry->ip_targets.ptr);
965     entry->ip_targets.ptr->ip = cptr->ip;
966     entry->ip_targets.ptr->free_targets = entry->free_targets;
967     entry->free_targets = HAS_TARGETS_MAGIC;
968     memcpy(entry->ip_targets.ptr->targets, cptr->targets, MAXTARGETS);
969     /*
970      * This calculation can be pretty unfair towards large multi-user hosts, but
971      * there is "nothing" we can do without also allowing spam bots to send more
972      * messages or by drastically increasing the ammount of memory used in the IPregistry.
973      *
974      * The problem is that when a client disconnects, leaving no free targets, then
975      * the next client from that IP number has to pay for it (getting no free targets).
976      * But ALSO the next client, and the next client, and the next client etc - until
977      * another client disconnects that DOES leave free targets.  The reason for this
978      * is that if there are 10 SPAM bots, and they all disconnect at once, then they
979      * ALL should get no free targets when reconnecting.  We'd need to store an entry
980      * per client (instead of per IP number) to avoid this.
981      */
982     if (cptr->nexttarget <= CurrentTime)
983         /*
984          * Number of free targets
985          */
986       inheritance = (CurrentTime - cptr->nexttarget) / TARGET_DELAY + 1;
987     else
988       inheritance = 0;
989     /*
990      * Add bonus, this is pretty fuzzy, but it will help in some cases.
991      */
992     if (CurrentTime - cptr->firsttime > 600)
993       /*
994        * Was longer then 10 minutes online?
995        */
996       inheritance += (CurrentTime - cptr->firsttime - 600) / TARGET_DELAY;
997     /*
998      * Finally, store smallest value for Judgement Day
999      */
1000     if (inheritance < entry->ip_targets.ptr->free_targets)
1001       entry->ip_targets.ptr->free_targets = inheritance;
1002   }
1003 }
1004
1005 /*
1006  * IPcheck_nr
1007  *
1008  * Returns number of clients with the same IP number
1009  */
1010 unsigned short IPcheck_nr(struct Client *cptr)
1011 {
1012   struct IPregistry *entry;
1013   CALCULATE_HASH(cptr->ip);
1014   entry = IPregistry_find(hash, cptr->ip);
1015   return (entry ? entry->connected : 0);
1016 }
1017 #endif