2 * charybdis: an advanced internet relay chat daemon (ircd).
3 * hostmask.c: Code to efficiently find IP & hostmask based configs.
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
8 * Copyright (C) 2005-2006 charybdis development team
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 #include "ircd_defs.h"
33 #include "irc_string.h"
36 static unsigned long hash_ipv6(struct sockaddr
*, int);
38 static unsigned long hash_ipv4(struct sockaddr
*, int);
41 /* int parse_netmask(const char *, struct irc_sockaddr_storage *, int *);
42 * Input: A hostmask, or an IPV4/6 address.
43 * Output: An integer describing whether it is an IPV4, IPV6 address or a
44 * hostmask, an address(if it is an IP mask),
45 * a bitlength(if it is IP mask).
49 parse_netmask(const char *text
, struct sockaddr
*naddr
, int *nb
)
51 char *ip
= LOCAL_COPY(text
);
53 struct irc_sockaddr_storage
*addr
, xaddr
;
61 addr
= (struct irc_sockaddr_storage
*)&xaddr
;
63 addr
= (struct irc_sockaddr_storage
*)naddr
;
68 if((ptr
= strchr(ip
, '/')))
77 if(inetpton_sock(ip
, (struct sockaddr
*)addr
) > 0)
85 if((ptr
= strchr(ip
, '/')))
94 if(inetpton_sock(ip
, (struct sockaddr
*)addr
) > 0)
102 /* Hashtable stuff...now external as its used in m_stats.c */
103 struct AddressRec
*atable
[ATABLE_SIZE
];
108 memset(&atable
, 0, sizeof(atable
));
111 /* unsigned long hash_ipv4(struct irc_sockaddr_storage*)
112 * Input: An IP address.
113 * Output: A hash value of the IP address.
117 hash_ipv4(struct sockaddr
*saddr
, int bits
)
119 struct sockaddr_in
*addr
= (struct sockaddr_in
*) saddr
;
123 unsigned long av
= ntohl(addr
->sin_addr
.s_addr
) & ~((1 << (32 - bits
)) - 1);
124 return (av
^ (av
>> 12) ^ (av
>> 24)) & (ATABLE_SIZE
- 1);
130 /* unsigned long hash_ipv6(struct irc_sockaddr_storage*)
131 * Input: An IP address.
132 * Output: A hash value of the IP address.
137 hash_ipv6(struct sockaddr
*saddr
, int bits
)
139 struct sockaddr_in6
*addr
= (struct sockaddr_in6
*) saddr
;
140 unsigned long v
= 0, n
;
141 for (n
= 0; n
< 16; n
++)
145 v
^= addr
->sin6_addr
.s6_addr
[n
];
150 v
^= addr
->sin6_addr
.s6_addr
[n
] & ~((1 << (8 - bits
)) - 1);
151 return v
& (ATABLE_SIZE
- 1);
154 return v
& (ATABLE_SIZE
- 1);
156 return v
& (ATABLE_SIZE
- 1);
160 /* int hash_text(const char *start)
161 * Input: The start of the text to hash.
162 * Output: The hash of the string between 1 and (TH_MAX-1)
163 * Side-effects: None.
166 hash_text(const char *start
)
168 const char *p
= start
;
173 h
= (h
<< 4) - (h
+ (unsigned char) ToLower(*p
++));
176 return (h
& (ATABLE_SIZE
- 1));
179 /* unsigned long get_hash_mask(const char *)
180 * Input: The text to hash.
181 * Output: The hash of the string right of the first '.' past the last
182 * wildcard in the string.
183 * Side-effects: None.
186 get_mask_hash(const char *text
)
188 const char *hp
= "", *p
;
190 for (p
= text
+ strlen(text
) - 1; p
>= text
; p
--)
191 if(*p
== '*' || *p
== '?')
192 return hash_text(hp
);
195 return hash_text(text
);
198 /* struct ConfItem* find_conf_by_address(const char*, struct irc_sockaddr_storage*,
199 * int type, int fam, const char *username)
200 * Input: The hostname, the address, the type of mask to find, the address
201 * family, the username.
202 * Output: The matching value with the highest precedence.
204 * Note: Setting bit 0 of the type means that the username is ignored.
207 find_conf_by_address(const char *name
, const char *sockhost
,
208 const char *orighost
,
209 struct sockaddr
*addr
, int type
, int fam
,
210 const char *username
)
212 unsigned long hprecv
= 0;
213 struct ConfItem
*hprec
= NULL
;
214 struct AddressRec
*arec
;
222 /* Check for IPV6 matches... */
227 for (b
= 128; b
>= 0; b
-= 16)
229 for (arec
= atable
[hash_ipv6(addr
, b
)]; arec
; arec
= arec
->next
)
230 if(arec
->type
== (type
& ~0x1) &&
231 arec
->masktype
== HM_IPV6
&&
232 comp_with_mask_sock(addr
, (struct sockaddr
*)&arec
->Mask
.ipa
.addr
,
233 arec
->Mask
.ipa
.bits
) && (type
& 0x1
238 && arec
->precedence
> hprecv
)
240 hprecv
= arec
->precedence
;
249 for (b
= 32; b
>= 0; b
-= 8)
251 for (arec
= atable
[hash_ipv4(addr
, b
)]; arec
; arec
= arec
->next
)
252 if(arec
->type
== (type
& ~0x1) &&
253 arec
->masktype
== HM_IPV4
&&
254 arec
->precedence
> hprecv
&&
255 comp_with_mask_sock(addr
, (struct sockaddr
*)&arec
->Mask
.ipa
.addr
,
256 arec
->Mask
.ipa
.bits
) &&
257 (type
& 0x1 || match(arec
->username
, username
)))
259 hprecv
= arec
->precedence
;
270 for (p
= orighost
; p
!= NULL
;)
272 for (arec
= atable
[hash_text(p
)]; arec
; arec
= arec
->next
)
274 if((arec
->type
== (type
& ~0x1)) &&
275 (arec
->masktype
== HM_HOST
) &&
276 arec
->precedence
> hprecv
&&
277 match(arec
->Mask
.hostname
, orighost
) &&
278 (type
& 0x1 || match(arec
->username
, username
)))
280 hprecv
= arec
->precedence
;
289 for (arec
= atable
[0]; arec
; arec
= arec
->next
)
291 if(arec
->type
== (type
& ~0x1) &&
292 arec
->masktype
== HM_HOST
&&
293 arec
->precedence
> hprecv
&&
294 (match(arec
->Mask
.hostname
, orighost
) ||
295 (sockhost
&& match(arec
->Mask
.hostname
, sockhost
))) &&
296 (type
& 0x1 || match(arec
->username
, username
)))
298 hprecv
= arec
->precedence
;
307 /* And yes - we have to check p after strchr and p after increment for
309 for (p
= name
; p
!= NULL
;)
311 for (arec
= atable
[hash_text(p
)]; arec
; arec
= arec
->next
)
312 if((arec
->type
== (type
& ~0x1)) &&
313 (arec
->masktype
== HM_HOST
) &&
314 arec
->precedence
> hprecv
&&
315 match(arec
->Mask
.hostname
, name
) &&
316 (type
& 0x1 || match(arec
->username
, username
)))
318 hprecv
= arec
->precedence
;
327 for (arec
= atable
[0]; arec
; arec
= arec
->next
)
329 if(arec
->type
== (type
& ~0x1) &&
330 arec
->masktype
== HM_HOST
&&
331 arec
->precedence
> hprecv
&&
332 (match(arec
->Mask
.hostname
, name
) ||
333 (sockhost
&& match(arec
->Mask
.hostname
, sockhost
))) &&
334 (type
& 0x1 || match(arec
->username
, username
)))
336 hprecv
= arec
->precedence
;
344 /* struct ConfItem* find_address_conf(const char*, const char*,
345 * struct irc_sockaddr_storage*, int);
346 * Input: The hostname, username, address, address family.
347 * Output: The applicable ConfItem.
351 find_address_conf(const char *host
, const char *sockhost
, const char *user
,
352 struct sockaddr
*ip
, int aftype
)
354 struct ConfItem
*iconf
, *kconf
;
356 /* Find the best I-line... If none, return NULL -A1kmm */
357 if(!(iconf
= find_conf_by_address(host
, sockhost
, NULL
, ip
, CONF_CLIENT
, aftype
, user
)))
360 /* If they are exempt from K-lines, return the best I-line. -A1kmm */
361 if(IsConfExemptKline(iconf
))
364 /* Find the best K-line... -A1kmm */
365 kconf
= find_conf_by_address(host
, sockhost
, NULL
, ip
, CONF_KILL
, aftype
, user
);
367 /* If they are K-lined, return the K-line */
371 /* if theres a spoof, check it against klines.. */
372 if(IsConfDoSpoofIp(iconf
))
374 char *p
= strchr(iconf
->name
, '@');
376 /* note, we dont need to pass sockhost here, as its
377 * guaranteed to not match by whats above.. --anfl
382 kconf
= find_conf_by_address(p
+1, NULL
, NULL
, ip
, CONF_KILL
, aftype
, iconf
->name
);
386 kconf
= find_conf_by_address(iconf
->name
, NULL
, NULL
, ip
, CONF_KILL
, aftype
, user
);
395 /* struct ConfItem* find_dline(struct irc_sockaddr_storage*, int)
396 * Input: An address, an address family.
397 * Output: The best matching D-line or exempt line.
398 * Side effects: None.
401 find_dline(struct sockaddr
*addr
, int aftype
)
403 struct ConfItem
*eline
;
404 eline
= find_conf_by_address(NULL
, NULL
, NULL
, addr
, CONF_EXEMPTDLINE
| 1, aftype
, NULL
);
407 return find_conf_by_address(NULL
, NULL
, NULL
, addr
, CONF_DLINE
| 1, aftype
, NULL
);
410 /* void add_conf_by_address(const char*, int, const char *,
411 * struct ConfItem *aconf)
414 * Side-effects: Adds this entry to the hash table.
417 add_conf_by_address(const char *address
, int type
, const char *username
, struct ConfItem
*aconf
)
419 static unsigned long prec_value
= 0xFFFFFFFF;
422 struct AddressRec
*arec
;
425 address
= "/NOMATCH!/";
426 arec
= MyMalloc(sizeof(struct AddressRec
));
427 masktype
= parse_netmask(address
, (struct sockaddr
*)&arec
->Mask
.ipa
.addr
, &bits
);
428 arec
->Mask
.ipa
.bits
= bits
;
429 arec
->masktype
= masktype
;
431 if(masktype
== HM_IPV6
)
433 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
435 arec
->next
= atable
[(hv
= hash_ipv6((struct sockaddr
*)&arec
->Mask
.ipa
.addr
, bits
))];
440 if(masktype
== HM_IPV4
)
442 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
444 arec
->next
= atable
[(hv
= hash_ipv4((struct sockaddr
*)&arec
->Mask
.ipa
.addr
, bits
))];
449 arec
->Mask
.hostname
= address
;
450 arec
->next
= atable
[(hv
= get_mask_hash(address
))];
453 arec
->username
= username
;
455 arec
->precedence
= prec_value
--;
459 /* void delete_one_address(const char*, struct ConfItem*)
460 * Input: An address string, the associated ConfItem.
462 * Side effects: Deletes an address record. Frees the ConfItem if there
463 * is nothing referencing it, sets it as illegal otherwise.
466 delete_one_address_conf(const char *address
, struct ConfItem
*aconf
)
470 struct AddressRec
*arec
, *arecl
= NULL
;
471 struct irc_sockaddr_storage addr
;
472 masktype
= parse_netmask(address
, (struct sockaddr
*)&addr
, &bits
);
474 if(masktype
== HM_IPV6
)
476 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
478 hv
= hash_ipv6((struct sockaddr
*)&addr
, bits
);
482 if(masktype
== HM_IPV4
)
484 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
486 hv
= hash_ipv4((struct sockaddr
*)&addr
, bits
);
489 hv
= get_mask_hash(address
);
490 for (arec
= atable
[hv
]; arec
; arec
= arec
->next
)
492 if(arec
->aconf
== aconf
)
495 arecl
->next
= arec
->next
;
497 atable
[hv
] = arec
->next
;
498 aconf
->status
|= CONF_ILLEGAL
;
508 /* void clear_out_address_conf(void)
511 * Side effects: Clears out all address records in the hash table,
512 * frees them, and frees the ConfItems if nothing references
513 * them, otherwise sets them as illegal.
516 clear_out_address_conf(void)
519 struct AddressRec
**store_next
;
520 struct AddressRec
*arec
, *arecn
;
522 for (i
= 0; i
< ATABLE_SIZE
; i
++)
524 store_next
= &atable
[i
];
525 for (arec
= atable
[i
]; arec
; arec
= arecn
)
528 /* We keep the temporary K-lines and destroy the
529 * permanent ones, just to be confusing :) -A1kmm */
530 if(arec
->aconf
->flags
& CONF_FLAGS_TEMPORARY
||
531 (arec
->type
!= CONF_CLIENT
&& arec
->type
!= CONF_EXEMPTDLINE
))
534 store_next
= &arec
->next
;
538 arec
->aconf
->status
|= CONF_ILLEGAL
;
539 if(!arec
->aconf
->clients
)
540 free_conf(arec
->aconf
);
549 clear_out_address_conf_bans(void)
552 struct AddressRec
**store_next
;
553 struct AddressRec
*arec
, *arecn
;
555 for (i
= 0; i
< ATABLE_SIZE
; i
++)
557 store_next
= &atable
[i
];
558 for (arec
= atable
[i
]; arec
; arec
= arecn
)
561 /* We keep the temporary K-lines and destroy the
562 * permanent ones, just to be confusing :) -A1kmm */
563 if(arec
->aconf
->flags
& CONF_FLAGS_TEMPORARY
||
564 (arec
->type
== CONF_CLIENT
|| arec
->type
== CONF_EXEMPTDLINE
))
567 store_next
= &arec
->next
;
571 arec
->aconf
->status
|= CONF_ILLEGAL
;
572 if(!arec
->aconf
->clients
)
573 free_conf(arec
->aconf
);
583 * show_iline_prefix()
585 * inputs - pointer to struct Client requesting output
586 * - pointer to struct ConfItem
587 * - name to which iline prefix will be prefixed to
588 * output - pointer to static string with prefixes listed in ascii form
589 * side effects - NONE
592 show_iline_prefix(struct Client
*sptr
, struct ConfItem
*aconf
, char *name
)
594 static char prefix_of_host
[USERLEN
+ 15];
597 prefix_ptr
= prefix_of_host
;
602 if(IsNeedIdentd(aconf
))
604 if(IsPassIdentd(aconf
))
606 if(IsNoMatchIp(aconf
))
608 if(IsConfDoSpoofIp(aconf
))
610 if(MyOper(sptr
) && IsConfExemptKline(aconf
))
612 if(MyOper(sptr
) && IsConfExemptLimits(aconf
))
614 if(MyOper(sptr
) && IsConfIdlelined(aconf
))
617 strncpy(prefix_ptr
, name
, USERLEN
);
618 return (prefix_of_host
);
623 * Inputs: pointer to client to report to
625 * Side effects: Reports configured auth{} blocks to client_p
628 report_auth(struct Client
*client_p
)
630 char *name
, *host
, *pass
, *user
, *classname
;
631 struct AddressRec
*arec
;
632 struct ConfItem
*aconf
;
635 for (i
= 0; i
< ATABLE_SIZE
; i
++)
636 for (arec
= atable
[i
]; arec
; arec
= arec
->next
)
637 if(arec
->type
== CONF_CLIENT
)
641 if(!MyOper(client_p
) && IsConfDoSpoofIp(aconf
))
644 get_printable_conf(aconf
, &name
, &host
, &pass
, &user
, &port
,
647 sendto_one_numeric(client_p
, RPL_STATSILINE
,
648 form_str(RPL_STATSILINE
),
649 name
, show_iline_prefix(client_p
, aconf
, user
),
650 show_ip_conf(aconf
, client_p
) ? host
: "255.255.255.255",
657 * inputs - Client to report to, mask
659 * side effects - Reports configured K-lines to client_p.
662 report_Klines(struct Client
*source_p
)
664 char *host
, *pass
, *user
, *oper_reason
;
665 struct AddressRec
*arec
;
666 struct ConfItem
*aconf
= NULL
;
669 for (i
= 0; i
< ATABLE_SIZE
; i
++)
671 for (arec
= atable
[i
]; arec
; arec
= arec
->next
)
673 if(arec
->type
== CONF_KILL
)
677 /* its a tempkline, theyre reported elsewhere */
678 if(aconf
->flags
& CONF_FLAGS_TEMPORARY
)
681 get_printable_kline(source_p
, aconf
, &host
, &pass
, &user
, &oper_reason
);
682 sendto_one_numeric(source_p
, RPL_STATSKLINE
,
683 form_str(RPL_STATSKLINE
),
684 'K', host
, user
, pass
,
685 oper_reason
? "|" : "",
686 oper_reason
? oper_reason
: "");