Author: Kev <klmitch@mit.edu>
[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 /** @file
20  * @brief Code to count users connected from particular IP addresses.
21  * @version $Id$
22  */
23 #include "config.h"
24
25 #include "IPcheck.h"
26 #include "client.h"
27 #include "ircd.h"
28 #include "match.h"
29 #include "msg.h"
30 #include "numnicks.h"       /* NumNick, NumServ (GODMODE) */
31 #include "ircd_alloc.h"
32 #include "ircd_events.h"
33 #include "ircd_features.h"
34 #include "ircd_log.h"
35 #include "s_debug.h"        /* Debug */
36 #include "s_user.h"         /* TARGET_DELAY */
37 #include "send.h"
38
39 /* #include <assert.h> -- Now using assert in ircd_log.h */
40 #include <string.h>
41
42 /** Stores free target information for a particular user. */
43 struct IPTargetEntry {
44   int           count; /**< Number of free targets targets. */
45   unsigned char targets[MAXTARGETS]; /**< Array of recent targets. */
46 };
47
48 /** Stores recent information about a particular IP address. */
49 struct IPRegistryEntry {
50   struct IPRegistryEntry*  next;   /**< Next entry in the hash chain. */
51   struct IPTargetEntry*    target; /**< Recent targets, if any. */
52   struct irc_in_addr       addr;   /**< IP address for this user. */
53   int                      last_connect; /**< Last connection attempt timestamp. */
54   unsigned short           connected; /**< Number of currently connected clients. */
55   unsigned char            attempts; /**< Number of recent connection attempts. */
56 };
57
58 /** Size of hash table (must be a power of two). */
59 #define IP_REGISTRY_TABLE_SIZE 0x10000
60 /** Report current time for tracking in IPRegistryEntry::last_connect. */
61 #define NOW ((unsigned short)(CurrentTime & 0xffff))
62 /** Time from \a x until now, in seconds. */
63 #define CONNECTED_SINCE(x) (NOW - (x))
64
65 /** Macro for easy access to configured IPcheck clone limit. */
66 #define IPCHECK_CLONE_LIMIT feature_int(FEAT_IPCHECK_CLONE_LIMIT)
67 /** Macro for easy access to configured IPcheck clone period. */
68 #define IPCHECK_CLONE_PERIOD feature_int(FEAT_IPCHECK_CLONE_PERIOD)
69 /** Macro for easy access to configured IPcheck clone delay. */
70 #define IPCHECK_CLONE_DELAY feature_int(FEAT_IPCHECK_CLONE_DELAY)
71
72 /** Hash table for storing IPRegistryEntry entries. */
73 static struct IPRegistryEntry* hashTable[IP_REGISTRY_TABLE_SIZE];
74 /** List of allocated but unused IPRegistryEntry structs. */
75 static struct IPRegistryEntry* freeList;
76 /** Periodic timer to look for too-old registry entries. */
77 static struct Timer expireTimer;
78
79 /** Calculate hash value for an IP address.
80  * If this looks like an IPv6 address, only consider the first 64 bits
81  * of the address. Otherwise, only consider the final 32 bits.
82  * @param[in] ip Address to hash.
83  * @return Hash value for address.
84  */
85 static unsigned int ip_registry_hash(const struct irc_in_addr *ip)
86 {
87   unsigned int res;
88
89   if (ip->in6_16[0] || ip->in6_16[1] || ip->in6_16[2] || ip->in6_16[3] || ip->in6_16[4]) {
90       /* Only use the first 64 bits of address, since the last 64 bits
91        * tend to be under user control. */
92       res = ip->in6_16[0] ^ ip->in6_16[1] ^ ip->in6_16[2] ^ ip->in6_16[3];
93   } else {
94       /* Looks like an IPv4 address. */
95       res = ip->in6_16[6] ^ ip->in6_16[7];
96   }
97   return res & (IP_REGISTRY_TABLE_SIZE - 1);
98 }
99
100 /** Find an IP registry entry if one exists for the IP address.
101  * If \a ip looks like an IPv6 address, only consider the first 64 bits
102  * of the address. Otherwise, only consider the final 32 bits.
103  * @param[in] ip IP address to search for.
104  * @return Matching registry entry, or NULL if none exists.
105  */
106 static struct IPRegistryEntry* ip_registry_find(const struct irc_in_addr *ip)
107 {
108   struct IPRegistryEntry* entry = hashTable[ip_registry_hash(ip)];
109   for ( ; entry; entry = entry->next) {
110     int bits = (ip->in6_16[0] || ip->in6_16[1] || ip->in6_16[2] || ip->in6_16[3] || ip->in6_16[4]) ? 64 : 128;
111     if (ipmask_check(ip, &entry->addr, bits))
112       break;
113   }
114   return entry;
115 }
116
117 /** Add an IP registry entry to the hash table.
118  * @param[in] entry Registry entry to add.
119  */
120 static void ip_registry_add(struct IPRegistryEntry* entry)
121 {
122   unsigned int bucket = ip_registry_hash(&entry->addr);
123   entry->next = hashTable[bucket];
124   hashTable[bucket] = entry;
125 }
126
127 /** Remove an IP registry entry from the hash table.
128  * @param[in] entry Registry entry to add.
129  */
130 static void ip_registry_remove(struct IPRegistryEntry* entry)
131 {
132   unsigned int bucket = ip_registry_hash(&entry->addr);
133   if (hashTable[bucket] == entry)
134     hashTable[bucket] = entry->next;
135   else {
136     struct IPRegistryEntry* prev = hashTable[bucket];
137     for ( ; prev; prev = prev->next) {
138       if (prev->next == entry) {
139         prev->next = entry->next;
140         break;
141       }
142     }
143   }
144 }
145
146 /** Allocate a new IP registry entry.
147  * For members that have a sensible default value, that is used.
148  * @return Newly allocated registry entry.
149  */
150 static struct IPRegistryEntry* ip_registry_new_entry()
151 {
152   struct IPRegistryEntry* entry = freeList;
153   if (entry)
154     freeList = entry->next;
155   else
156     entry = (struct IPRegistryEntry*) MyMalloc(sizeof(struct IPRegistryEntry));
157
158   assert(0 != entry);
159   memset(entry, 0, sizeof(struct IPRegistryEntry));
160   entry->last_connect = NOW;     /* Seconds since last connect attempt */
161   entry->connected    = 1;       /* connected clients for this IP */
162   entry->attempts     = 1;       /* Number attempts for this IP */
163   return entry;
164 }
165
166 /** Deallocate memory for \a entry.
167  * The entry itself is prepended to #freeList.
168  * @param[in] entry IP registry entry to release.
169  */
170 static void ip_registry_delete_entry(struct IPRegistryEntry* entry)
171 {
172   if (entry->target)
173     MyFree(entry->target);
174   entry->next = freeList;
175   freeList = entry;
176 }
177
178 /** Update free target count for \a entry.
179  * @param[in,out] entry IP registry entry to update.
180  */
181 static unsigned int ip_registry_update_free_targets(struct IPRegistryEntry* entry)
182 {
183   unsigned int free_targets = STARTTARGETS;
184
185   if (entry->target) {
186     free_targets = entry->target->count + (CONNECTED_SINCE(entry->last_connect) / TARGET_DELAY);
187     if (free_targets > STARTTARGETS)
188       free_targets = STARTTARGETS;
189     entry->target->count = free_targets;
190   }
191   return free_targets;
192 }
193
194 /** Check whether all or part of \a entry needs to be expired.
195  * If the entry is at least 600 seconds stale, free the entire thing.
196  * If it is at least 120 seconds stale, expire its free targets list.
197  * @param[in] entry Registry entry to check for expiration.
198  */
199 static void ip_registry_expire_entry(struct IPRegistryEntry* entry)
200 {
201   /*
202    * Don't touch this number, it has statistical significance
203    * XXX - blah blah blah
204    */
205   if (CONNECTED_SINCE(entry->last_connect) > 600) {
206     /*
207      * expired
208      */
209     ip_registry_remove(entry);
210     ip_registry_delete_entry(entry);
211   }
212   else if (CONNECTED_SINCE(entry->last_connect) > 120 && 0 != entry->target) {
213     /*
214      * Expire storage of targets
215      */
216     MyFree(entry->target);
217     entry->target = 0;
218   }
219 }
220
221 /** Periodic timer callback to check for expired registry entries.
222  * @param[in] ev Timer event (ignored).
223  */
224 static void ip_registry_expire(struct Event* ev)
225 {
226   int i;
227   struct IPRegistryEntry* entry;
228   struct IPRegistryEntry* entry_next;
229
230   assert(ET_EXPIRE == ev_type(ev));
231   assert(0 != ev_timer(ev));
232
233   for (i = 0; i < IP_REGISTRY_TABLE_SIZE; ++i) {
234     for (entry = hashTable[i]; entry; entry = entry_next) {
235       entry_next = entry->next;
236       if (0 == entry->connected)
237         ip_registry_expire_entry(entry);
238     }
239   }
240 }
241
242 /** Initialize the IPcheck subsystem. */
243 void IPcheck_init(void)
244 {
245   timer_add(timer_init(&expireTimer), ip_registry_expire, 0, TT_PERIODIC, 60);
246 }
247
248 /** Check whether a new connection from a local client should be allowed.
249  * A connection is rejected if someone from the "same" address (see
250  * ip_registry_find()) connects IPCHECK_CLONE_LIMIT times, each time
251  * separated by no more than IPCHECK_CLONE_PERIOD seconds.
252  * @param[in] addr Address of client.
253  * @param[out] next_target_out Receives time to grant another free target.
254  * @return Non-zero if the connection is permitted, zero if denied.
255  */
256 int ip_registry_check_local(const struct irc_in_addr *addr, time_t* next_target_out)
257 {
258   struct IPRegistryEntry* entry = ip_registry_find(addr);
259   unsigned int free_targets = STARTTARGETS;
260
261   if (0 == entry) {
262     entry       = ip_registry_new_entry();
263     memcpy(&entry->addr, addr, sizeof(entry->addr));
264     ip_registry_add(entry);
265     return 1;
266   }
267   /* Note that this also connects server connects.
268    * It is hard and not interesting, to change that.
269    *
270    * Don't allow more then 255 connects from one IP number, ever
271    */
272   if (0 == ++entry->connected)
273   {
274     entry->connected--;
275     return 0;
276   }
277
278   if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_PERIOD)
279     entry->attempts = 0;
280
281   free_targets = ip_registry_update_free_targets(entry);
282   entry->last_connect = NOW;
283
284   if (0 == ++entry->attempts)   /* Check for overflow */
285     --entry->attempts;
286
287   if (entry->attempts < IPCHECK_CLONE_LIMIT) {
288     if (next_target_out)
289       *next_target_out = CurrentTime - (TARGET_DELAY * free_targets - 1);
290   }
291   else if ((CurrentTime - cli_since(&me)) > IPCHECK_CLONE_DELAY) {
292     /* 
293      * Don't refuse connection when we just rebooted the server
294      */
295 #ifdef NOTHROTTLE 
296     return 1;
297 #else
298     assert(entry->connected > 0);
299     --entry->connected;
300     return 0;
301 #endif        
302   }
303   return 1;
304 }
305
306 /** Check whether a connection from a remote client should be allowed.
307  * This is much more relaxed than ip_registry_check_local(): The only
308  * cause for rejection is when the IPRegistryEntry::connected counter
309  * would overflow.
310  * @param[in] cptr Client that has connected.
311  * @param[in] is_burst Non-zero if client was introduced during a burst.
312  * @return Non-zero if the client should be accepted, zero if they must be killed.
313  */
314 int ip_registry_check_remote(struct Client* cptr, int is_burst)
315 {
316   struct IPRegistryEntry* entry = ip_registry_find(&cli_ip(cptr));
317
318   /*
319    * Mark that we did add/update an IPregistry entry
320    */
321   SetIPChecked(cptr);
322   if (0 == entry) {
323     entry = ip_registry_new_entry();
324     memcpy(&entry->addr, &cli_ip(cptr), sizeof(entry->addr));
325     if (is_burst)
326       entry->attempts = 0;
327     ip_registry_add(entry);
328   }
329   else {
330     if (0 == ++entry->connected) {
331       /* 
332        * Don't allow more then 255 connects from one IP number, ever
333        */
334       return 0;
335     }
336     if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_PERIOD)
337       entry->attempts = 0;
338     if (!is_burst) {
339       if (0 == ++entry->attempts) {
340         /*
341          * Check for overflow
342          */
343         --entry->attempts;
344       }
345       ip_registry_update_free_targets(entry);
346       entry->last_connect = NOW;
347     }
348   }
349   return 1;
350 }
351
352 /** Handle a client being rejected during connection through no fault
353  * of their own.  This "undoes" the effect of ip_registry_check_local()
354  * so the client's address is not penalized for the failure.
355  * @param[in] addr Address of rejected client.
356  */
357 void ip_registry_connect_fail(const struct irc_in_addr *addr)
358 {
359   struct IPRegistryEntry* entry = ip_registry_find(addr);
360   if (entry)
361   {
362     if (0 == --entry->attempts)
363       ++entry->attempts;
364   }
365 }
366
367 /** Handle a client that has successfully connected.
368  * This copies free target information to \a cptr from his address's
369  * registry entry and sends him a NOTICE describing the parameters for
370  * the entry.
371  * @param[in,out] cptr Client that has successfully connected.
372  */
373 void ip_registry_connect_succeeded(struct Client *cptr)
374 {
375   const char*             tr    = "";
376   unsigned int free_targets     = STARTTARGETS;
377   struct IPRegistryEntry* entry = ip_registry_find(&cli_ip(cptr));
378
379   if (!entry) {
380     Debug((DEBUG_ERROR, "Missing registry entry for: %s", cli_sock_ip(cptr)));
381     return;
382   }
383   if (entry->target) {
384     memcpy(cli_targets(cptr), entry->target->targets, MAXTARGETS);
385     free_targets = entry->target->count;
386     tr = " tr";
387   }
388   sendcmdto_one(&me, CMD_NOTICE, cptr, "%C :on %u ca %u(%u) ft %u(%u)%s",
389                 cptr, entry->connected, entry->attempts, IPCHECK_CLONE_LIMIT,
390                 free_targets, STARTTARGETS, tr);
391 }
392
393 /** Handle a client that decided to disconnect (or was killed after
394  * completing his connection).  This updates the free target
395  * information for his IP registry entry.
396  * @param[in] cptr Client that has exited.
397  */
398 void ip_registry_disconnect(struct Client *cptr)
399 {
400   struct IPRegistryEntry* entry = ip_registry_find(&cli_ip(cptr));
401   if (0 == entry) {
402     /*
403      * trying to find an entry for a server causes this to happen,
404      * servers should never have FLAG_IPCHECK set
405      */
406     return;
407   }
408   /*
409    * If this was the last one, set `last_connect' to disconnect time (used for expiration)
410    */
411   /* assert(entry->connected > 0); */
412   if (0 == --entry->connected) {
413     if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD) {
414       /*
415        * Otherwise we'd penetalize for this old value if the client reconnects within 20 seconds
416        */
417       entry->attempts = 0;
418     }
419     ip_registry_update_free_targets(entry);
420     entry->last_connect = NOW;
421   }
422   if (MyConnect(cptr)) {
423     unsigned int free_targets;
424     /*
425      * Copy the clients targets
426      */
427     if (0 == entry->target) {
428       entry->target = (struct IPTargetEntry*) MyMalloc(sizeof(struct IPTargetEntry));
429       entry->target->count = STARTTARGETS;
430     }
431     assert(0 != entry->target);
432
433     memcpy(entry->target->targets, cli_targets(cptr), MAXTARGETS);
434     /*
435      * This calculation can be pretty unfair towards large multi-user hosts, but
436      * there is "nothing" we can do without also allowing spam bots to send more
437      * messages or by drastically increasing the ammount of memory used in the IPregistry.
438      *
439      * The problem is that when a client disconnects, leaving no free targets, then
440      * the next client from that IP number has to pay for it (getting no free targets).
441      * But ALSO the next client, and the next client, and the next client etc - until
442      * another client disconnects that DOES leave free targets.  The reason for this
443      * is that if there are 10 SPAM bots, and they all disconnect at once, then they
444      * ALL should get no free targets when reconnecting.  We'd need to store an entry
445      * per client (instead of per IP number) to avoid this.
446      */
447     if (cli_nexttarget(cptr) < CurrentTime) {
448         /*
449          * Number of free targets
450          */
451       free_targets = (CurrentTime - cli_nexttarget(cptr)) / TARGET_DELAY + 1;
452     }
453     else
454       free_targets = 0;
455     /*
456      * Add bonus, this is pretty fuzzy, but it will help in some cases.
457      */
458     if ((CurrentTime - cli_firsttime(cptr)) > 600)
459       /*
460        * Was longer then 10 minutes online?
461        */
462       free_targets += (CurrentTime - cli_firsttime(cptr) - 600) / TARGET_DELAY;
463     /*
464      * Finally, store smallest value for Judgement Day
465      */
466     if (free_targets < entry->target->count)
467       entry->target->count = free_targets;
468   }
469 }
470
471 /** Find number of clients from a particular IP address.
472  * @param[in] addr Address to look up.
473  * @return Number of clients known to be connected from that address.
474  */
475 int ip_registry_count(const struct irc_in_addr *addr)
476 {
477   struct IPRegistryEntry* entry = ip_registry_find(addr);
478   return (entry) ? entry->connected : 0;
479 }
480
481 /** Check whether a client is allowed to connect locally.
482  * @param[in] a Address of client.
483  * @param[out] next_target_out Receives time to grant another free target.
484  * @return Non-zero if the connection is permitted, zero if denied.
485  */
486 int IPcheck_local_connect(const struct irc_in_addr *a, time_t* next_target_out)
487 {
488   assert(0 != next_target_out);
489   return ip_registry_check_local(a, next_target_out);
490 }
491
492 /** Check whether a client is allowed to connect remotely.
493  * @param[in] cptr Client that has connected.
494  * @param[in] is_burst Non-zero if client was introduced during a burst.
495  * @return Non-zero if the client should be accepted, zero if they must be killed.
496  */
497 int IPcheck_remote_connect(struct Client *cptr, int is_burst)
498 {
499   assert(0 != cptr);
500   return ip_registry_check_remote(cptr, is_burst);
501 }
502
503 /** Handle a client being rejected during connection through no fault
504  * of their own.  This "undoes" the effect of ip_registry_check_local()
505  * so the client's address is not penalized for the failure.
506  * @param[in] a Address of rejected client.
507  */
508 void IPcheck_connect_fail(const struct irc_in_addr *a)
509 {
510   ip_registry_connect_fail(a);
511 }
512
513 /** Handle a client that has successfully connected.
514  * This copies free target information to \a cptr from his address's
515  * registry entry and sends him a NOTICE describing the parameters for
516  * the entry.
517  * @param[in,out] cptr Client that has successfully connected.
518  */
519 void IPcheck_connect_succeeded(struct Client *cptr)
520 {
521   assert(0 != cptr);
522   ip_registry_connect_succeeded(cptr);
523 }
524
525 /** Handle a client that decided to disconnect (or was killed after
526  * completing his connection).  This updates the free target
527  * information for his IP registry entry.
528  * @param[in] cptr Client that has exited.
529  */
530 void IPcheck_disconnect(struct Client *cptr)
531 {
532   assert(0 != cptr);
533   ip_registry_disconnect(cptr);
534 }
535
536 /** Find number of clones of a client.
537  * @param[in] cptr Client whose address to look up.
538  * @return Number of clients known to be connected from that address.
539  */
540 unsigned short IPcheck_nr(struct Client *cptr)
541 {
542   assert(0 != cptr);
543   return ip_registry_count(&cli_ip(cptr));
544 }