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