X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2FIPcheck.c;h=67b34851cb8fe0bbe4ce905040c4ff1179e81c83;hb=refs%2Fheads%2Fupstream;hp=f867f745d9a86b1cdc24f2e1433deb38eb06e574;hpb=5713fadcc7efab3a4170a465040f6003a8c0f6cc;p=ircu2.10.12-pk.git diff --git a/ircd/IPcheck.c b/ircd/IPcheck.c index f867f74..67b3485 100644 --- a/ircd/IPcheck.c +++ b/ircd/IPcheck.c @@ -15,140 +15,139 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - * */ +/** @file + * @brief Code to count users connected from particular IP addresses. + * @version $Id$ + */ +#include "config.h" + #include "IPcheck.h" #include "client.h" #include "ircd.h" -#include "numnicks.h" -#include "ircd_alloc.h" +#include "match.h" #include "msg.h" -#include "s_bsd.h" -#include "s_debug.h" -#include "s_user.h" +#include "ircd_alloc.h" +#include "ircd_events.h" +#include "ircd_features.h" +#include "ircd_log.h" +#include "ircd_string.h" /* ircd_ntoa */ +#include "s_debug.h" /* Debug */ +#include "s_user.h" /* TARGET_DELAY */ #include "send.h" -#include -#include -#include +/* #include -- Now using assert in ircd_log.h */ #include -#if 0 -#warning Nick collisions are horribly broken in -#warning this version, and its known to core on -#warning a whim. If your even concidering -#warning running this on something resembling a -#warning production network, dont bother, its -#warning not worth your time. To those of you -#warning who grabbed the latest CVS version to -#warning bug test it, thanks, but I recommend -#warning you stick to previous versions for the -#warning time being. -#error --- Broken code --- -#endif - +/** Stores free target information for a particular user. */ struct IPTargetEntry { - int count; - unsigned char targets[MAXTARGETS]; + unsigned int count; /**< Number of free targets targets. */ + unsigned char targets[MAXTARGETS]; /**< Array of recent targets. */ }; +/** Stores recent information about a particular IP address. */ struct IPRegistryEntry { - struct IPRegistryEntry *next; - struct IPTargetEntry *target; - unsigned int addr; - time_t last_connect; - unsigned char connected; - unsigned char attempts; + struct IPRegistryEntry* next; /**< Next entry in the hash chain. */ + struct IPTargetEntry* target; /**< Recent targets, if any. */ + struct irc_in_addr addr; /**< IP address for this user. */ + int last_connect; /**< Last connection attempt timestamp. */ + unsigned short connected; /**< Number of currently connected clients. */ + unsigned char attempts; /**< Number of recent connection attempts. */ }; - -/* - * Hash table for IPv4 address registry - * - * Hash table size must be a power of 2 - * Use 64K hash table to conserve memory - */ -/*---------------------------------------------------------------------------- - * Compile-time Configuration - *--------------------------------------------------------------------------*/ +/** Size of hash table (must be a power of two). */ #define IP_REGISTRY_TABLE_SIZE 0x10000 -#define MASK_16 0xffff - -/* We allow 6 connections in 60 seconds */ -#define IPCHECK_CLONE_LIMIT 6 -#define IPCHECK_CLONE_PERIOD 60 -#define IPCHECK_CLONE_DELAY 600 - - -/*---------------------------------------------------------------------------- - * Handy Macros - *--------------------------------------------------------------------------*/ -#define NOW (CurrentTime) -#define CONNECTED_SINCE(x) (NOW - (x->last_connect)) - - -/*---------------------------------------------------------------------------- - * Global Data (ugly!) - *--------------------------------------------------------------------------*/ -static struct IPRegistryEntry *hashTable[IP_REGISTRY_TABLE_SIZE]; -static struct IPRegistryEntry *freeList = 0; - - -/*---------------------------------------------------------------------------- - * ip_registry_hash: Create a hash key for an IP registry entry and return - * the value. (Is unsigned int really a good type to give - * to the IP argument? Ugly. This should probably be a - * struct in_addr. This is asking for trouble. --ZS) - *--------------------------------------------------------------------------*/ -static unsigned int ip_registry_hash(unsigned int ip) +/** Report current time for tracking in IPRegistryEntry::last_connect. */ +#define NOW ((unsigned short)(CurrentTime & 0xffff)) +/** Time from \a x until now, in seconds. */ +#define CONNECTED_SINCE(x) (NOW - (x)) + +/** Macro for easy access to configured IPcheck clone limit. */ +#define IPCHECK_CLONE_LIMIT feature_int(FEAT_IPCHECK_CLONE_LIMIT) +/** Macro for easy access to configured IPcheck clone period. */ +#define IPCHECK_CLONE_PERIOD feature_int(FEAT_IPCHECK_CLONE_PERIOD) +/** Macro for easy access to configured IPcheck clone delay. */ +#define IPCHECK_CLONE_DELAY feature_int(FEAT_IPCHECK_CLONE_DELAY) + +/** Hash table for storing IPRegistryEntry entries. */ +static struct IPRegistryEntry* hashTable[IP_REGISTRY_TABLE_SIZE]; +/** List of allocated but unused IPRegistryEntry structs. */ +static struct IPRegistryEntry* freeList; +/** Periodic timer to look for too-old registry entries. */ +static struct Timer expireTimer; + +/** Convert IP addresses to canonical form for comparison. IPv4 + * addresses are translated into 6to4 form; IPv6 addresses are left + * alone. + * @param[out] out Receives canonical format for address. + * @param[in] in IP address to canonicalize. + */ +static void ip_registry_canonicalize(struct irc_in_addr *out, const struct irc_in_addr *in) { - return ((ip >> 16) ^ ip) & (IP_REGISTRY_TABLE_SIZE - 1); + if (irc_in_addr_is_ipv4(in)) { + out->in6_16[0] = htons(0x2002); + out->in6_16[1] = in->in6_16[6]; + out->in6_16[2] = in->in6_16[7]; + out->in6_16[3] = out->in6_16[4] = out->in6_16[5] = 0; + out->in6_16[6] = out->in6_16[7] = 0; + } else + memcpy(out, in, sizeof(*out)); } - -/*---------------------------------------------------------------------------- - * ip_registry_find: Find a given IP registry entry and return it. - *--------------------------------------------------------------------------*/ -static struct IPRegistryEntry *ip_registry_find(unsigned int ip) +/** Calculate hash value for an IP address. + * @param[in] ip Address to hash; must be in canonical form. + * @return Hash value for address. + */ +static unsigned int ip_registry_hash(const struct irc_in_addr *ip) { - struct IPRegistryEntry *entry = 0; + unsigned int res; + /* Only use the first 64 bits of address, since the last 64 bits + * tend to be under user control. */ + res = ip->in6_16[0] ^ ip->in6_16[1] ^ ip->in6_16[2] ^ ip->in6_16[3]; + return res & (IP_REGISTRY_TABLE_SIZE - 1); +} - for (entry = hashTable[ip_registry_hash(ip)]; entry; entry = entry->next) { - if (entry->addr == ip) - return entry; +/** Find an IP registry entry if one exists for the IP address. + * If \a ip looks like an IPv6 address, only consider the first 64 bits + * of the address. Otherwise, only consider the final 32 bits. + * @param[in] ip IP address to search for. + * @return Matching registry entry, or NULL if none exists. + */ +static struct IPRegistryEntry* ip_registry_find(const struct irc_in_addr *ip) +{ + struct irc_in_addr canon; + struct IPRegistryEntry* entry; + ip_registry_canonicalize(&canon, ip); + entry = hashTable[ip_registry_hash(&canon)]; + for ( ; entry; entry = entry->next) { + int bits = (canon.in6_16[0] == htons(0x2002)) ? 48 : 64; + if (ipmask_check(&canon, &entry->addr, bits)) + break; } - - return NULL; + return entry; } - -/*---------------------------------------------------------------------------- - * ip_registry_add: Add an entry to the IP registry - *--------------------------------------------------------------------------*/ -static void ip_registry_add(struct IPRegistryEntry *entry) +/** Add an IP registry entry to the hash table. + * @param[in] entry Registry entry to add. + */ +static void ip_registry_add(struct IPRegistryEntry* entry) { - unsigned int bucket = ip_registry_hash(entry->addr); - + unsigned int bucket = ip_registry_hash(&entry->addr); entry->next = hashTable[bucket]; hashTable[bucket] = entry; } - -/*---------------------------------------------------------------------------- - * ip_registry_remove: Remove an entry from the IP registry - *--------------------------------------------------------------------------*/ -static void ip_registry_remove(struct IPRegistryEntry* entry) +/** Remove an IP registry entry from the hash table. + * @param[in] entry Registry entry to add. + */ +static void ip_registry_remove(struct IPRegistryEntry* entry) { - unsigned int bucket = ip_registry_hash(entry->addr); - + unsigned int bucket = ip_registry_hash(&entry->addr); if (hashTable[bucket] == entry) hashTable[bucket] = entry->next; else { - struct IPRegistryEntry *prev; - - for (prev = hashTable[bucket]; prev; prev = prev->next) { + struct IPRegistryEntry* prev = hashTable[bucket]; + for ( ; prev; prev = prev->next) { if (prev->next == entry) { prev->next = entry->next; break; @@ -156,109 +155,94 @@ static void ip_registry_remove(struct IPRegistryEntry* entry) } } } - -/*---------------------------------------------------------------------------- - * ip_registry_new_entry(): Creates and initializes an IP Registry entry. - * NOW ALSO ADDS IT TO THE LIST! --ZS - *--------------------------------------------------------------------------*/ -static struct IPRegistryEntry *ip_registry_new_entry(unsigned int addr, int attempt) +/** Allocate a new IP registry entry. + * For members that have a sensible default value, that is used. + * @return Newly allocated registry entry. + */ +static struct IPRegistryEntry* ip_registry_new_entry(void) { struct IPRegistryEntry* entry = freeList; - if (entry) freeList = entry->next; else - entry = (struct IPRegistryEntry *)MyMalloc(sizeof(struct IPRegistryEntry)); + entry = (struct IPRegistryEntry*) MyMalloc(sizeof(struct IPRegistryEntry)); assert(0 != entry); - memset(entry, 0, sizeof(struct IPRegistryEntry)); entry->last_connect = NOW; /* Seconds since last connect attempt */ entry->connected = 1; /* connected clients for this IP */ - entry->attempts = attempt; /* Number attempts for this IP */ - entry->addr = addr; /* Entry's IP Address */ - - ip_registry_add(entry); - + entry->attempts = 1; /* Number attempts for this IP */ return entry; } - -/*---------------------------------------------------------------------------- - * ip_registry_delete_entry: Frees an entry and adds the structure to a list - * of free structures. (We should probably reclaim - * the freelist every once in a while! This is - * potentially a way to DoS the server... -ZS) - *--------------------------------------------------------------------------*/ -static void ip_registry_delete_entry(struct IPRegistryEntry *entry) +/** Deallocate memory for \a entry. + * The entry itself is prepended to #freeList. + * @param[in] entry IP registry entry to release. + */ +static void ip_registry_delete_entry(struct IPRegistryEntry* entry) { if (entry->target) MyFree(entry->target); - entry->next = freeList; freeList = entry; } - -/*---------------------------------------------------------------------------- - * ip_registry_update_free_targets: - *--------------------------------------------------------------------------*/ -static unsigned int ip_registry_update_free_targets(struct IPRegistryEntry *entry) +/** Update free target count for \a entry. + * @param[in,out] entry IP registry entry to update. + */ +static unsigned int ip_registry_update_free_targets(struct IPRegistryEntry* entry) { unsigned int free_targets = STARTTARGETS; if (entry->target) { - free_targets = (entry->target->count + - (CONNECTED_SINCE(entry) / TARGET_DELAY)); - + free_targets = entry->target->count + (CONNECTED_SINCE(entry->last_connect) / TARGET_DELAY); if (free_targets > STARTTARGETS) free_targets = STARTTARGETS; - entry->target->count = free_targets; } - return free_targets; } - -/*---------------------------------------------------------------------------- - * ip_registry_expire_entry: expire an IP entry if it needs to be. If an - * entry isn't expired, then also check the target - * list to see if it needs to be expired. - *--------------------------------------------------------------------------*/ -static void ip_registry_expire_entry(struct IPRegistryEntry *entry) +/** Check whether all or part of \a entry needs to be expired. + * If the entry is at least 600 seconds stale, free the entire thing. + * If it is at least 120 seconds stale, expire its free targets list. + * @param[in] entry Registry entry to check for expiration. + */ +static void ip_registry_expire_entry(struct IPRegistryEntry* entry) { /* * Don't touch this number, it has statistical significance * XXX - blah blah blah - * ZS - Just -what- statistical significance does it -have-? - * Iso - Noone knows, we've just been told not to touch it. */ - if (CONNECTED_SINCE(entry) > 120 && 0 != entry->target) { - MyFree(entry->target); - entry->target = 0; - } - if (CONNECTED_SINCE(entry) > 600) { + if (CONNECTED_SINCE(entry->last_connect) > 600) { + /* + * expired + */ + Debug((DEBUG_DNS, "IPcheck expiring registry for %s (no clients connected).", ircd_ntoa(&entry->addr))); ip_registry_remove(entry); ip_registry_delete_entry(entry); } + else if (CONNECTED_SINCE(entry->last_connect) > 120 && 0 != entry->target) { + /* + * Expire storage of targets + */ + MyFree(entry->target); + entry->target = 0; + } } - -/*---------------------------------------------------------------------------- - * ip_registry_expire: Expire all of the needed entries in the hash table - *--------------------------------------------------------------------------*/ -void ip_registry_expire(void) +/** Periodic timer callback to check for expired registry entries. + * @param[in] ev Timer event (ignored). + */ +static void ip_registry_expire(struct Event* ev) { - struct IPRegistryEntry *entry; - struct IPRegistryEntry *entry_next; - static time_t next_expire = 0; int i; + struct IPRegistryEntry* entry; + struct IPRegistryEntry* entry_next; - /* Only do this if we're ready to */ - if (next_expire >= CurrentTime) - return; + assert(ET_EXPIRE == ev_type(ev)); + assert(0 != ev_timer(ev)); for (i = 0; i < IP_REGISTRY_TABLE_SIZE; ++i) { for (entry = hashTable[i]; entry; entry = entry_next) { @@ -267,331 +251,322 @@ void ip_registry_expire(void) ip_registry_expire_entry(entry); } } - - next_expire = CurrentTime + 60; } +/** Initialize the IPcheck subsystem. */ +void IPcheck_init(void) +{ + timer_add(timer_init(&expireTimer), ip_registry_expire, 0, TT_PERIODIC, 60); +} -/*---------------------------------------------------------------------------- - * - * Event: - * A new connection was accept()-ed with IP number `cptr->ip.s_addr'. - * - * Action: - * Update the IPcheck registry. - * Return: - * 1 : You're allowed to connect. - * 0 : You're not allowed to connect. - * - * Throttling: - * - * A connection should be rejected when a connection from the same IP - * number was received IPCHECK_CLONE_LIMIT times before this connect - * attempt, with reconnect intervals of IPCHECK_CLONE_PERIOD seconds - * or less. - * - * Free target inheritance: - * - * When the client is accepted, then the number of Free Targets - * of the cptr is set to the value stored in the found IPregistry - * structure, or left at STARTTARGETS. This can be done by changing - * cptr->nexttarget to be `now - (TARGET_DELAY * (FREE_TARGETS - 1))', - * where FREE_TARGETS may range from 0 till STARTTARGETS. - *--------------------------------------------------------------------------*/ -int ip_registry_check_local(unsigned int addr, time_t *next_target_out) +/** Check whether a new connection from a local client should be allowed. + * A connection is rejected if someone from the "same" address (see + * ip_registry_find()) connects IPCHECK_CLONE_LIMIT times, each time + * separated by no more than IPCHECK_CLONE_PERIOD seconds. + * @param[in] addr Address of client. + * @param[out] next_target_out Receives time to grant another free target. + * @return Non-zero if the connection is permitted, zero if denied. + */ +int ip_registry_check_local(const struct irc_in_addr *addr, time_t* next_target_out) { - struct IPRegistryEntry *entry = ip_registry_find(addr); + struct IPRegistryEntry* entry = ip_registry_find(addr); unsigned int free_targets = STARTTARGETS; - - assert(0 != next_target_out); - /* If they've never connected before, let them on */ if (0 == entry) { - Debug((DEBUG_DEBUG,"IPcheck: Local user allowed - unseen")); - entry = ip_registry_new_entry(addr, 1); + entry = ip_registry_new_entry(); + ip_registry_canonicalize(&entry->addr, addr); + ip_registry_add(entry); + Debug((DEBUG_DNS, "IPcheck added new registry for local connection from %s.", ircd_ntoa(&entry->addr))); return 1; } - - /* Keep track of how many people have connected */ - entry->connected++; - - /* Do not allow more than 250 connects from a single IP, EVER. */ - if (250 <= entry->connected) { - Debug((DEBUG_DEBUG,"IPcheck: Local user disallowed - Too many connections")); + /* Note that this also counts server connects. + * It is hard and not interesting, to change that. + * Refuse connection if it would overflow the counter. + */ + if (0 == ++entry->connected) + { entry->connected--; + Debug((DEBUG_DNS, "IPcheck refusing local connection from %s: counter overflow.", ircd_ntoa(&entry->addr))); return 0; } - /* If our threshhold has elapsed, reset the counter so we don't throttle, - * IPCHECK_CLONE_LIMIT connections every IPCHECK_CLONE_PERIOD - */ - if (CONNECTED_SINCE(entry) > IPCHECK_CLONE_PERIOD) { + if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_PERIOD) entry->attempts = 0; - entry->last_connect = NOW; - } - - /* Count the number of recent attempts */ - entry->attempts++; - - if (250 <= entry->attempts) - --entry->attempts; /* Disallow overflow */ - free_targets = ip_registry_update_free_targets(entry); + entry->last_connect = NOW; - /* Have they connected less than IPCHECK_CLONE_LIMIT times && next_target_out */ - if (entry->attempts < IPCHECK_CLONE_LIMIT && next_target_out) { + if (0 == ++entry->attempts) /* Check for overflow */ + --entry->attempts; + + if (entry->attempts < IPCHECK_CLONE_LIMIT) { + if (next_target_out) *next_target_out = CurrentTime - (TARGET_DELAY * free_targets - 1); - entry->last_connect = NOW; - Debug((DEBUG_DEBUG,"IPcheck: Local user allowed")); - return 1; } - - /* If the server is younger than IPCHECK_CLONE_DELAY then the person - * is allowed on. - */ - if ((CurrentTime - me.since) < IPCHECK_CLONE_DELAY) { - Debug((DEBUG_DEBUG,"IPcheck: Local user allowed during server startup")); - return 1; + else if ((CurrentTime - cli_since(&me)) > IPCHECK_CLONE_DELAY) { + /* + * Don't refuse connection when we just rebooted the server + */ +#ifndef NOTHROTTLE + assert(entry->connected > 0); + --entry->connected; + Debug((DEBUG_DNS, "IPcheck refusing local connection from %s: too fast.", ircd_ntoa(&entry->addr))); + return 0; +#endif } - - /* Otherwise they're throttled */ - entry->connected--; - Debug((DEBUG_DEBUG,"IPcheck: Throttling local user")); - return 0; + Debug((DEBUG_DNS, "IPcheck accepting local connection from %s.", ircd_ntoa(&entry->addr))); + return 1; } -/* - * Add someone to the ip registry without throttling them. - * This is used for server connections. +/** Check whether a connection from a remote client should be allowed. + * This is much more relaxed than ip_registry_check_local(): The only + * cause for rejection is when the IPRegistryEntry::connected counter + * would overflow. + * @param[in] cptr Client that has connected. + * @param[in] is_burst Non-zero if client was introduced during a burst. + * @return Non-zero if the client should be accepted, zero if they must be killed. */ -void ip_registry_add_local(unsigned int addr) +int ip_registry_check_remote(struct Client* cptr, int is_burst) { - struct IPRegistryEntry *entry = ip_registry_find(addr); - - /* If they've never connected before, let them on */ - if (0 == entry) { - Debug((DEBUG_DEBUG,"IPcheck: Local user allowed - unseen")); - entry = ip_registry_new_entry(addr, 1); - return; - } - - /* Keep track of how many people have connected */ - entry->connected++; - - assert(250 <= entry->connected); - - return; -} - -/*---------------------------------------------------------------------------- - * ip_registry_remote_connect - * - * Does anything that needs to be done once we actually have a client - * structure to play with on a remote connection. - * returns: - * 1 - allowed to connect - * 0 - disallowed. - *--------------------------------------------------------------------------*/ -int ip_registry_remote_connect(struct Client *cptr) -{ - struct IPRegistryEntry *entry = ip_registry_find(cptr->ip.s_addr); - assert(0 != cptr); + struct IPRegistryEntry* entry; - /* If they've never connected before, let them on */ + /* + * Mark that we did add/update an IPregistry entry + */ + SetIPChecked(cptr); + if (!irc_in_addr_valid(&cli_ip(cptr))) { + Debug((DEBUG_DNS, "IPcheck accepting remote connection from invalid %s.", ircd_ntoa(&cli_ip(cptr)))); + return 1; + } + entry = ip_registry_find(&cli_ip(cptr)); if (0 == entry) { - entry = ip_registry_new_entry(cptr->ip.s_addr, 1); - SetIPChecked(cptr); - Debug((DEBUG_DEBUG,"IPcheck: First remote connection. connected=%i",entry->connected)); + entry = ip_registry_new_entry(); + ip_registry_canonicalize(&entry->addr, &cli_ip(cptr)); + if (is_burst) + entry->attempts = 0; + ip_registry_add(entry); + Debug((DEBUG_DNS, "IPcheck added new registry for remote connection from %s.", ircd_ntoa(&entry->addr))); return 1; } - - /* Keep track of how many people have connected */ - entry->connected++; - SetIPChecked(cptr); - - /* Do not allow more than 250 connections from one IP. - * This can happen by having 128 clients on one server, and 128 on another - * and then the servers joining after a netsplit - */ - if (250 <= entry->connected) { - sendto_ops("IPcheck Ghost! [%s]",inet_ntoa(cptr->ip)); - Debug((DEBUG_DEBUG,"IPcheck: Too many connected from IP: %i",entry->connected)); + /* Avoid overflowing the connection counter. */ + if (0 == ++entry->connected) { + Debug((DEBUG_DNS, "IPcheck refusing remote connection from %s: counter overflow.", ircd_ntoa(&entry->addr))); return 0; } - - Debug((DEBUG_DEBUG,"IPcheck: %i people connected",entry->connected)); - - /* They are allowed to connect */ + if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_PERIOD) + entry->attempts = 0; + if (!is_burst) { + if (0 == ++entry->attempts) { + /* + * Check for overflow + */ + --entry->attempts; + } + ip_registry_update_free_targets(entry); + entry->last_connect = NOW; + } + Debug((DEBUG_DNS, "IPcheck counting remote connection from %s.", ircd_ntoa(&entry->addr))); return 1; } -/*---------------------------------------------------------------------------- - * IPcheck_connect_succeeded - * - * Event: - * A client succeeded to finish the registration. - * - * Finish IPcheck registration of a successfully, locally connected client. - *--------------------------------------------------------------------------*/ +/** Handle a client being rejected during connection through no fault + * of their own. This "undoes" the effect of ip_registry_check_local() + * so the client's address is not penalized for the failure. + * @param[in] addr Address of rejected client. + * @param[in] disconnect If true, also count the client as disconnecting. + */ +void ip_registry_connect_fail(const struct irc_in_addr *addr, int disconnect) +{ + struct IPRegistryEntry* entry = ip_registry_find(addr); + if (entry) { + if (0 == --entry->attempts) { + Debug((DEBUG_DNS, "IPcheck noting local connection failure for %s.", ircd_ntoa(&entry->addr))); + ++entry->attempts; + } + if (disconnect) { + assert(entry->connected > 0); + entry->connected--; + } + } +} + +/** Handle a client that has successfully connected. + * This copies free target information to \a cptr from his address's + * registry entry and sends him a NOTICE describing the parameters for + * the entry. + * @param[in,out] cptr Client that has successfully connected. + */ void ip_registry_connect_succeeded(struct Client *cptr) { + const char* tr = ""; unsigned int free_targets = STARTTARGETS; - struct IPRegistryEntry *entry; - - assert(cptr); - - entry = ip_registry_find(cptr->ip.s_addr); - + struct IPRegistryEntry* entry = ip_registry_find(&cli_ip(cptr)); assert(entry); - if (entry->target) { - memcpy(cptr->targets, entry->target->targets, MAXTARGETS); + memcpy(cli_targets(cptr), entry->target->targets, MAXTARGETS); free_targets = entry->target->count; + tr = " tr"; } - - sendcmdto_one(&me, CMD_NOTICE, cptr, "%C :connected %u attempts %u/%u free targets %u/%u%s" - " IPcheck: %s", + Debug((DEBUG_DNS, "IPcheck noting local connection success for %s.", ircd_ntoa(&entry->addr))); + sendcmdto_one(&me, CMD_NOTICE, cptr, "%C :on %u ca %u(%u) ft %u(%u)%s", cptr, entry->connected, entry->attempts, IPCHECK_CLONE_LIMIT, - free_targets, STARTTARGETS, - ((entry->target) ? " [Inherited Targets]" : ""), - ((CurrentTime - me.since) < IPCHECK_CLONE_DELAY) ? "Disabled" : "Enabled"); - - SetIPChecked(cptr); + free_targets, STARTTARGETS, tr); } - -/*---------------------------------------------------------------------------- - * IPcheck_disconnect - * - * Event: - * A local client disconnected. - * - * Action: - * Update the IPcheck registry. - * Remove all expired IPregistry structures from the hash bucket - * that belongs to this clients IP number. - *--------------------------------------------------------------------------*/ -void ip_registry_local_disconnect(struct Client *cptr) +/** Handle a client that decided to disconnect (or was killed after + * completing his connection). This updates the free target + * information for his IP registry entry. + * @param[in] cptr Client that has exited. + */ +void ip_registry_disconnect(struct Client *cptr) { - struct IPRegistryEntry *entry; - unsigned int free_targets; - - assert(0 != cptr); - - entry = ip_registry_find(cptr->ip.s_addr); - - Debug((DEBUG_DEBUG,"IPcheck: Local Disconnect")); - - assert(IsIPChecked(cptr)); - + struct IPRegistryEntry* entry = ip_registry_find(&cli_ip(cptr)); + if (!irc_in_addr_valid(&cli_ip(cptr))) { + Debug((DEBUG_DNS, "IPcheck noting dicconnect from invalid %s.", ircd_ntoa(&cli_ip(cptr)))); + return; + } assert(entry); - assert(entry->connected > 0); - - if (entry->connected > 0) { - entry->connected--; - } - + Debug((DEBUG_DNS, "IPcheck noting disconnect from %s.", ircd_ntoa(&entry->addr))); /* - * If this was the last one, set `last_connect' to disconnect time - * (used for expiration) Note that we reset attempts here as well if our - * threshhold hasn't been crossed. + * If this was the last one, set `last_connect' to disconnect time (used for expiration) */ - if (0 == entry->connected) { + if (0 == --entry->connected) { + if (CONNECTED_SINCE(entry->last_connect) > IPCHECK_CLONE_LIMIT * IPCHECK_CLONE_PERIOD) { + /* + * Otherwise we'd penalize for this old value if the client reconnects within 20 seconds + */ + entry->attempts = 0; + } ip_registry_update_free_targets(entry); entry->last_connect = NOW; } - - assert(MyConnect(cptr)); - - if (0 == entry->target) { - entry->target = (struct IPTargetEntry *)MyMalloc(sizeof(struct IPTargetEntry)); + if (MyConnect(cptr)) { + unsigned int free_targets; + /* + * Copy the clients targets + */ + if (0 == entry->target) { + entry->target = (struct IPTargetEntry*) MyMalloc(sizeof(struct IPTargetEntry)); + entry->target->count = STARTTARGETS; + } assert(0 != entry->target); - entry->target->count = STARTTARGETS; - } - memcpy(entry->target->targets, cptr->targets, MAXTARGETS); - - /* - * This calculation can be pretty unfair towards large multi-user hosts, - * but there is "nothing" we can do without also allowing spam bots to - * send more messages or by drastically increasing the ammount of memory - * used in the IPregistry. - * - * The problem is that when a client disconnects, leaving no free targets, - * then the next client from that IP number has to pay for it (getting no - * free targets). But ALSO the next client, and the next client, and the - * next client etc - until another client disconnects that DOES leave free - * targets. The reason for this is that if there are 10 SPAM bots, and - * they all disconnect at once, then they ALL should get no free targets - * when reconnecting. We'd need to store an entry per client (instead of - * per IP number) to avoid this. - */ - if (cptr->nexttarget < CurrentTime) - free_targets = (CurrentTime - cptr->nexttarget) / TARGET_DELAY + 1; - else - free_targets = 0; - /* Add bonus, if you've been connected for more than 10 minutes you - * get a free target every TARGET_DELAY seconds. - * this is pretty fuzzy, but it will help in some cases. - */ - if ((CurrentTime - cptr->firsttime) > 600) - free_targets += (CurrentTime - cptr->firsttime - 600) / TARGET_DELAY; + memcpy(entry->target->targets, cli_targets(cptr), MAXTARGETS); + /* + * This calculation can be pretty unfair towards large multi-user hosts, but + * there is "nothing" we can do without also allowing spam bots to send more + * messages or by drastically increasing the amount of memory used in the IPregistry. + * + * The problem is that when a client disconnects, leaving no free targets, then + * the next client from that IP number has to pay for it (getting no free targets). + * But ALSO the next client, and the next client, and the next client etc - until + * another client disconnects that DOES leave free targets. The reason for this + * is that if there are 10 SPAM bots, and they all disconnect at once, then they + * ALL should get no free targets when reconnecting. We'd need to store an entry + * per client (instead of per IP number) to avoid this. + */ + if (cli_nexttarget(cptr) < CurrentTime) { + /* + * Number of free targets + */ + free_targets = (CurrentTime - cli_nexttarget(cptr)) / TARGET_DELAY + 1; + } + else + free_targets = 0; + /* + * Add bonus, this is pretty fuzzy, but it will help in some cases. + */ + if ((CurrentTime - cli_firsttime(cptr)) > 600) + /* + * Was longer then 10 minutes online? + */ + free_targets += (CurrentTime - cli_firsttime(cptr) - 600) / TARGET_DELAY; + /* + * Finally, store smallest value for Judgment Day + */ + if (free_targets < entry->target->count) + entry->target->count = free_targets; + } +} - /* Finally, store smallest value for Judgement Day */ - if (free_targets < entry->target->count) - entry->target->count = free_targets; - +/** Find number of clients from a particular IP address. + * @param[in] addr Address to look up. + * @return Number of clients known to be connected from that address. + */ +int ip_registry_count(const struct irc_in_addr *addr) +{ + struct IPRegistryEntry* entry = ip_registry_find(addr); + return (entry) ? entry->connected : 0; } -/*---------------------------------------------------------------------------- - * ip_registry_remote_disconnect - * - * Event: - * A remote client disconnected. - * - * Action: - * Update the IPcheck registry. - * Remove all expired IPregistry structures from the hash bucket - * that belongs to this clients IP number. - *--------------------------------------------------------------------------*/ -void ip_registry_remote_disconnect(struct Client *cptr) +/** Check whether a client is allowed to connect locally. + * @param[in] a Address of client. + * @param[out] next_target_out Receives time to grant another free target. + * @return Non-zero if the connection is permitted, zero if denied. + */ +int IPcheck_local_connect(const struct irc_in_addr *a, time_t* next_target_out) { - struct IPRegistryEntry *entry; + assert(0 != next_target_out); + return ip_registry_check_local(a, next_target_out); +} +/** Check whether a client is allowed to connect remotely. + * @param[in] cptr Client that has connected. + * @param[in] is_burst Non-zero if client was introduced during a burst. + * @return Non-zero if the client should be accepted, zero if they must be killed. + */ +int IPcheck_remote_connect(struct Client *cptr, int is_burst) +{ assert(0 != cptr); + assert(!IsIPChecked(cptr)); + return ip_registry_check_remote(cptr, is_burst); +} - entry = ip_registry_find(cptr->ip.s_addr); - - assert(entry); - - assert(entry->connected > 0); - Debug((DEBUG_DEBUG,"IPcheck: Remote Disconnect")); +/** Handle a client being rejected during connection through no fault + * of their own. This "undoes" the effect of ip_registry_check_local() + * so the client's address is not penalized for the failure. + * @param[in] cptr Client who has been rejected. + * @param[in] disconnect If true, also count the client as disconnecting. + */ +void IPcheck_connect_fail(const struct Client *cptr, int disconnect) +{ + assert(IsIPChecked(cptr)); + ip_registry_connect_fail(&cli_ip(cptr), disconnect); +} - if (entry->connected > 0) { - entry->connected--; - } +/** Handle a client that has successfully connected. + * This copies free target information to \a cptr from his address's + * registry entry and sends him a NOTICE describing the parameters for + * the entry. + * @param[in,out] cptr Client that has successfully connected. + */ +void IPcheck_connect_succeeded(struct Client *cptr) +{ + assert(0 != cptr); + assert(IsIPChecked(cptr)); + ip_registry_connect_succeeded(cptr); +} - /* - * If this was the last one, set `last_connect' to disconnect time - * (used for expiration) Note that we reset attempts here as well if our - * threshhold hasn't been crossed. - */ - if (0 == entry->connected) { - ip_registry_update_free_targets(entry); - entry->last_connect=NOW; - } +/** Handle a client that decided to disconnect (or was killed after + * completing his connection). This updates the free target + * information for his IP registry entry. + * @param[in] cptr Client that has exited. + */ +void IPcheck_disconnect(struct Client *cptr) +{ + assert(0 != cptr); + assert(IsIPChecked(cptr)); + ip_registry_disconnect(cptr); } -/*---------------------------------------------------------------------------- - * IPcheck_nr - * - * Returns number of clients with the same IP number - *--------------------------------------------------------------------------*/ -int ip_registry_count(unsigned int addr) +/** Find number of clones of a client. + * @param[in] cptr Client whose address to look up. + * @return Number of clients known to be connected from that address. + */ +unsigned short IPcheck_nr(struct Client *cptr) { - struct IPRegistryEntry *entry = ip_registry_find(addr); - return (entry) ? entry->connected : 0; + assert(0 != cptr); + return ip_registry_count(&cli_ip(cptr)); }