2 * Solanum: a slightly advanced 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-2008 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
27 #include "ircd_defs.h"
29 #include "s_newconf.h"
35 static unsigned long hash_ipv6(struct sockaddr
*, int);
36 static unsigned long hash_ipv4(struct sockaddr
*, int);
40 _parse_netmask(const char *text
, struct rb_sockaddr_storage
*naddr
, int *nb
, bool strict
)
42 char *ip
= LOCAL_COPY(text
);
45 struct rb_sockaddr_storage
*addr
, xaddr
;
57 if(strpbrk(ip
, "*?") != NULL
)
63 if((ptr
= strchr(ip
, '/')))
67 long n
= strtol(ptr
, &endp
, 10);
68 if (endp
== ptr
|| n
< 0)
70 if (n
> 128 || *endp
!= '\0')
80 if(rb_inet_pton_sock(ip
, addr
) > 0)
87 if((ptr
= strchr(ip
, '/')))
91 long n
= strtol(ptr
, &endp
, 10);
92 if (endp
== ptr
|| n
< 0)
94 if (n
> 32 || *endp
!= '\0')
104 if(rb_inet_pton_sock(ip
, addr
) > 0)
112 /* int parse_netmask(const char *, struct rb_sockaddr_storage *, int *);
113 * Input: A hostmask, or an IPV4/6 address.
114 * Output: An integer describing whether it is an IPV4, IPV6 address or a
115 * hostmask, an address(if it is an IP mask),
116 * a bitlength(if it is IP mask).
119 int parse_netmask(const char *mask
, struct rb_sockaddr_storage
*addr
, int *blen
)
121 return _parse_netmask(mask
, addr
, blen
, false);
124 int parse_netmask_strict(const char *mask
, struct rb_sockaddr_storage
*addr
, int *blen
)
126 return _parse_netmask(mask
, addr
, blen
, true);
129 /* Hashtable stuff...now external as its used in m_stats.c */
130 struct AddressRec
*atable
[ATABLE_SIZE
];
135 memset(&atable
, 0, sizeof(atable
));
138 /* unsigned long hash_ipv4(struct rb_sockaddr_storage*)
139 * Input: An IP address.
140 * Output: A hash value of the IP address.
144 hash_ipv4(struct sockaddr
*saddr
, int bits
)
146 struct sockaddr_in
*addr
= (struct sockaddr_in
*)(void *)saddr
;
150 unsigned long av
= ntohl(addr
->sin_addr
.s_addr
) & ~((1 << (32 - bits
)) - 1);
151 return (av
^ (av
>> 12) ^ (av
>> 24)) & (ATABLE_SIZE
- 1);
157 /* unsigned long hash_ipv6(struct rb_sockaddr_storage*)
158 * Input: An IP address.
159 * Output: A hash value of the IP address.
163 hash_ipv6(struct sockaddr
*saddr
, int bits
)
165 struct sockaddr_in6
*addr
= (struct sockaddr_in6
*)(void *)saddr
;
166 unsigned long v
= 0, n
;
167 for (n
= 0; n
< 16; n
++)
171 v
^= addr
->sin6_addr
.s6_addr
[n
];
176 v
^= addr
->sin6_addr
.s6_addr
[n
] & ~((1 << (8 - bits
)) - 1);
177 return v
& (ATABLE_SIZE
- 1);
180 return v
& (ATABLE_SIZE
- 1);
182 return v
& (ATABLE_SIZE
- 1);
185 /* int hash_text(const char *start)
186 * Input: The start of the text to hash.
187 * Output: The hash of the string between 1 and (TH_MAX-1)
188 * Side-effects: None.
191 hash_text(const char *start
)
193 const char *p
= start
;
198 h
= (h
<< 4) - (h
+ (unsigned char) irctolower(*p
++));
201 return (h
& (ATABLE_SIZE
- 1));
204 /* unsigned long get_hash_mask(const char *)
205 * Input: The text to hash.
206 * Output: The hash of the string right of the first '.' past the last
207 * wildcard in the string.
208 * Side-effects: None.
211 get_mask_hash(const char *text
)
213 const char *hp
= "", *p
;
215 for (p
= text
+ strlen(text
) - 1; p
>= text
; p
--)
216 if(*p
== '*' || *p
== '?')
217 return hash_text(hp
);
220 return hash_text(text
);
223 /* struct ConfItem* find_conf_by_address(const char*, struct rb_sockaddr_storage*,
224 * int type, int fam, const char *username)
226 * This process needs to be kept in sync with check_one_kline().
228 * Input: The hostname, the address, the type of mask to find, the address
229 * family, the username.
230 * Output: The matching value with the highest precedence.
232 * Note: Setting bit 0 of the type means that the username is ignored.
235 find_conf_by_address(const char *name
, const char *sockhost
,
236 const char *orighost
,
237 struct sockaddr
*addr
, int type
, int fam
,
238 const char *username
, const char *auth_user
)
240 unsigned long hprecv
= 0;
241 struct ConfItem
*hprec
= NULL
;
242 struct AddressRec
*arec
;
243 struct sockaddr_in ip4
;
244 struct sockaddr
*pip4
= NULL
;
257 if (type
== CONF_KILL
&& rb_ipv4_from_ipv6((struct sockaddr_in6
*)addr
, &ip4
))
258 pip4
= (struct sockaddr
*)&ip4
;
260 for (b
= 128; b
>= 0; b
-= 16)
262 for (arec
= atable
[hash_ipv6(addr
, b
)]; arec
; arec
= arec
->next
)
263 if(arec
->type
== (type
& ~0x1) &&
264 arec
->masktype
== HM_IPV6
&&
265 comp_with_mask_sock(addr
, (struct sockaddr
*)&arec
->Mask
.ipa
.addr
,
266 arec
->Mask
.ipa
.bits
) &&
267 (type
& 0x1 || match(arec
-> username
, username
)) &&
268 (type
!= CONF_CLIENT
|| !arec
->auth_user
||
269 (auth_user
&& match(arec
->auth_user
, auth_user
))) &&
270 arec
->precedence
> hprecv
)
272 hprecv
= arec
->precedence
;
280 for (b
= 32; b
>= 0; b
-= 8)
282 for (arec
= atable
[hash_ipv4(pip4
, b
)]; arec
; arec
= arec
->next
)
283 if(arec
->type
== (type
& ~0x1) &&
284 arec
->masktype
== HM_IPV4
&&
285 comp_with_mask_sock(pip4
, (struct sockaddr
*)&arec
->Mask
.ipa
.addr
,
286 arec
->Mask
.ipa
.bits
) &&
287 (type
& 0x1 || match(arec
->username
, username
)) &&
288 (type
!= CONF_CLIENT
|| !arec
->auth_user
||
289 (auth_user
&& match(arec
->auth_user
, auth_user
))) &&
290 arec
->precedence
> hprecv
)
292 hprecv
= arec
->precedence
;
303 for (p
= orighost
; p
!= NULL
;)
305 for (arec
= atable
[hash_text(p
)]; arec
; arec
= arec
->next
)
307 if((arec
->type
== (type
& ~0x1)) &&
308 (arec
->masktype
== HM_HOST
) &&
309 arec
->precedence
> hprecv
&&
310 match(arec
->Mask
.hostname
, orighost
) &&
311 (type
!= CONF_CLIENT
|| !arec
->auth_user
||
312 (auth_user
&& match(arec
->auth_user
, auth_user
))) &&
313 (type
& 0x1 || match(arec
->username
, username
)))
315 hprecv
= arec
->precedence
;
324 for (arec
= atable
[0]; arec
; arec
= arec
->next
)
326 if(arec
->type
== (type
& ~0x1) &&
327 arec
->masktype
== HM_HOST
&&
328 arec
->precedence
> hprecv
&&
329 (match(arec
->Mask
.hostname
, orighost
) ||
330 (sockhost
&& match(arec
->Mask
.hostname
, sockhost
))) &&
331 (type
!= CONF_CLIENT
|| !arec
->auth_user
||
332 (auth_user
&& match(arec
->auth_user
, auth_user
))) &&
333 (type
& 0x1 || match(arec
->username
, username
)))
335 hprecv
= arec
->precedence
;
344 /* And yes - we have to check p after strchr and p after increment for
346 for (p
= name
; p
!= NULL
;)
348 for (arec
= atable
[hash_text(p
)]; arec
; arec
= arec
->next
)
349 if((arec
->type
== (type
& ~0x1)) &&
350 (arec
->masktype
== HM_HOST
) &&
351 arec
->precedence
> hprecv
&&
352 match(arec
->Mask
.hostname
, name
) &&
353 (type
!= CONF_CLIENT
|| !arec
->auth_user
||
354 (auth_user
&& match(arec
->auth_user
, auth_user
))) &&
355 (type
& 0x1 || match(arec
->username
, username
)))
357 hprecv
= arec
->precedence
;
366 for (arec
= atable
[0]; arec
; arec
= arec
->next
)
368 if(arec
->type
== (type
& ~0x1) &&
369 arec
->masktype
== HM_HOST
&&
370 arec
->precedence
> hprecv
&&
371 (match(arec
->Mask
.hostname
, name
) ||
372 (sockhost
&& match(arec
->Mask
.hostname
, sockhost
))) &&
373 (type
!= CONF_CLIENT
|| !arec
->auth_user
||
374 (auth_user
&& match(arec
->auth_user
, auth_user
))) &&
375 (type
& 0x1 || match(arec
->username
, username
)))
377 hprecv
= arec
->precedence
;
385 /* struct ConfItem* find_address_conf(const char*, const char*,
386 * struct rb_sockaddr_storage*, int);
387 * Input: The hostname, username, address, address family.
388 * Output: The applicable ConfItem.
392 find_address_conf(const char *host
, const char *sockhost
, const char *user
,
393 const char *notildeuser
, struct sockaddr
*ip
, int aftype
, char *auth_user
)
395 struct ConfItem
*iconf
, *kconf
;
398 /* Find the best I-line... If none, return NULL -A1kmm */
399 if(!(iconf
= find_conf_by_address(host
, sockhost
, NULL
, ip
, CONF_CLIENT
, aftype
, user
, auth_user
)))
401 /* Find what their visible username will be.
402 * Note that the username without tilde may contain one char more.
404 vuser
= IsNoTilde(iconf
) ? notildeuser
: user
;
406 /* If they are exempt from K-lines, return the best I-line. -A1kmm */
407 if(IsConfExemptKline(iconf
))
410 /* if theres a spoof, check it against klines.. */
411 if(IsConfDoSpoofIp(iconf
))
413 char *p
= strchr(iconf
->info
.name
, '@');
415 /* note, we dont need to pass sockhost here, as its
416 * guaranteed to not match by whats below.. --anfl
421 kconf
= find_conf_by_address(p
+1, NULL
, NULL
, NULL
, CONF_KILL
, aftype
, iconf
->info
.name
, NULL
);
425 kconf
= find_conf_by_address(iconf
->info
.name
, NULL
, NULL
, NULL
, CONF_KILL
, aftype
, vuser
, NULL
);
430 /* everything else checks real hosts, if they're kline_spoof_ip we're done */
431 if(IsConfKlineSpoof(iconf
))
435 /* Find the best K-line... -A1kmm */
436 kconf
= find_conf_by_address(host
, sockhost
, NULL
, ip
, CONF_KILL
, aftype
, user
, NULL
);
438 /* If they are K-lined, return the K-line */
442 /* if no_tilde, check the username without tilde against klines too
446 kconf
= find_conf_by_address(host
, sockhost
, NULL
, ip
, CONF_KILL
, aftype
, vuser
, NULL
);
454 /* struct ConfItem* find_dline(struct rb_sockaddr_storage*, int)
455 * Input: An address, an address family.
456 * Output: The best matching D-line or exempt line.
457 * Side effects: None.
460 find_dline(struct sockaddr
*addr
, int aftype
)
462 struct ConfItem
*aconf
;
463 struct sockaddr_in addr2
;
465 aconf
= find_conf_by_address(NULL
, NULL
, NULL
, addr
, CONF_EXEMPTDLINE
| 1, aftype
, NULL
, NULL
);
468 aconf
= find_conf_by_address(NULL
, NULL
, NULL
, addr
, CONF_DLINE
| 1, aftype
, NULL
, NULL
);
471 if(addr
->sa_family
== AF_INET6
&&
472 rb_ipv4_from_ipv6((const struct sockaddr_in6
*)(const void *)addr
, &addr2
))
474 aconf
= find_conf_by_address(NULL
, NULL
, NULL
, (struct sockaddr
*)&addr2
, CONF_DLINE
| 1, AF_INET
, NULL
, NULL
);
482 find_exact_conf_by_address_filtered(const char *address
, int type
, const char *username
, bool (*filter
)(struct ConfItem
*))
486 struct AddressRec
*arec
;
487 struct rb_sockaddr_storage addr
;
490 address
= "/NOMATCH!/";
491 masktype
= parse_netmask(address
, &addr
, &bits
);
492 if(masktype
== HM_IPV6
)
494 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
495 hv
= hash_ipv6((struct sockaddr
*)&addr
, bits
- bits
% 16);
497 else if(masktype
== HM_IPV4
)
499 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
500 hv
= hash_ipv4((struct sockaddr
*)&addr
, bits
- bits
% 8);
504 hv
= get_mask_hash(address
);
506 for (arec
= atable
[hv
]; arec
; arec
= arec
->next
)
508 if (arec
->type
== type
&&
509 arec
->masktype
== masktype
&&
510 (arec
->username
== NULL
|| username
== NULL
? arec
->username
== username
: !irccmp(arec
->username
, username
)))
512 if (filter
&& !filter(arec
->aconf
))
515 if (masktype
== HM_HOST
)
517 if (!irccmp(arec
->Mask
.hostname
, address
))
522 if (arec
->Mask
.ipa
.bits
== bits
&&
523 comp_with_mask_sock((struct sockaddr
*)&arec
->Mask
.ipa
.addr
, (struct sockaddr
*)&addr
, bits
))
531 /* void find_exact_conf_by_address(const char*, int, const char *)
533 * Output: ConfItem if found
537 find_exact_conf_by_address(const char *address
, int type
, const char *username
)
539 return find_exact_conf_by_address_filtered(address
, type
, username
, NULL
);
542 /* void add_conf_by_address(const char*, int, const char *,
543 * struct ConfItem *aconf)
546 * Side-effects: Adds this entry to the hash table.
549 add_conf_by_address(const char *address
, int type
, const char *username
, const char *auth_user
, struct ConfItem
*aconf
)
551 static unsigned long prec_value
= 0xFFFFFFFF;
554 struct AddressRec
*arec
;
557 address
= "/NOMATCH!/";
558 arec
= rb_malloc(sizeof(struct AddressRec
));
559 arec
->masktype
= parse_netmask(address
, &arec
->Mask
.ipa
.addr
, &bits
);
560 if(arec
->masktype
== HM_IPV6
)
562 arec
->Mask
.ipa
.bits
= bits
;
563 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
565 arec
->next
= atable
[(hv
= hash_ipv6((struct sockaddr
*)&arec
->Mask
.ipa
.addr
, bits
))];
568 else if(arec
->masktype
== HM_IPV4
)
570 arec
->Mask
.ipa
.bits
= bits
;
571 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
573 arec
->next
= atable
[(hv
= hash_ipv4((struct sockaddr
*)&arec
->Mask
.ipa
.addr
, bits
))];
578 arec
->Mask
.hostname
= address
;
579 arec
->next
= atable
[(hv
= get_mask_hash(address
))];
582 arec
->username
= username
;
583 arec
->auth_user
= auth_user
;
585 arec
->precedence
= prec_value
--;
589 /* void delete_one_address(const char*, struct ConfItem*)
590 * Input: An address string, the associated ConfItem.
592 * Side effects: Deletes an address record. Frees the ConfItem if there
593 * is nothing referencing it, sets it as illegal otherwise.
596 delete_one_address_conf(const char *address
, struct ConfItem
*aconf
)
600 struct AddressRec
*arec
, *arecl
= NULL
;
601 struct rb_sockaddr_storage addr
;
602 masktype
= parse_netmask(address
, &addr
, &bits
);
603 if(masktype
== HM_IPV6
)
605 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
607 hv
= hash_ipv6((struct sockaddr
*)&addr
, bits
);
609 else if(masktype
== HM_IPV4
)
611 /* We have to do this, since we do not re-hash for every bit -A1kmm. */
613 hv
= hash_ipv4((struct sockaddr
*)&addr
, bits
);
616 hv
= get_mask_hash(address
);
617 for (arec
= atable
[hv
]; arec
; arec
= arec
->next
)
619 if(arec
->aconf
== aconf
)
622 arecl
->next
= arec
->next
;
624 atable
[hv
] = arec
->next
;
625 aconf
->status
|= CONF_ILLEGAL
;
635 /* void clear_out_address_conf(void)
638 * Side effects: Clears out all address records in the hash table,
639 * frees them, and frees the ConfItems if nothing references
640 * them, otherwise sets them as illegal.
643 clear_out_address_conf(enum aconf_category clear_type
)
646 struct AddressRec
**store_next
;
647 struct AddressRec
*arec
, *arecn
;
649 for (i
= 0; i
< ATABLE_SIZE
; i
++)
651 store_next
= &atable
[i
];
652 for (arec
= atable
[i
]; arec
; arec
= arecn
)
654 enum aconf_category cur_type
;
657 if (arec
->type
== CONF_CLIENT
|| arec
->type
== CONF_EXEMPTDLINE
|| arec
->type
== CONF_SECURE
)
658 cur_type
= AC_CONFIG
;
662 /* We keep the temporary K-lines and destroy the
663 * permanent ones, just to be confusing :) -A1kmm */
664 if (arec
->aconf
->flags
& CONF_FLAGS_TEMPORARY
|| cur_type
!= clear_type
)
667 store_next
= &arec
->next
;
671 arec
->aconf
->status
|= CONF_ILLEGAL
;
672 if(!arec
->aconf
->clients
)
673 free_conf(arec
->aconf
);
682 * show_iline_prefix()
684 * inputs - pointer to struct Client requesting output
685 * - pointer to struct ConfItem
686 * - name to which iline prefix will be prefixed to
687 * output - pointer to static string with prefixes listed in ascii form
688 * side effects - NONE
691 show_iline_prefix(struct Client
*sptr
, struct ConfItem
*aconf
, char *name
)
693 static char prefix_of_host
[USERLEN
+ 15];
696 prefix_ptr
= prefix_of_host
;
699 if(IsNeedIdentd(aconf
))
701 if(IsConfDoSpoofIp(aconf
))
703 if(IsNeedSasl(aconf
))
705 if(IsOper(sptr
) && IsConfExemptFlood(aconf
))
707 if(IsOper(sptr
) && IsConfExemptDNSBL(aconf
) && !IsConfExemptKline(aconf
))
709 if(IsOper(sptr
) && IsConfExemptKline(aconf
))
711 if(IsOper(sptr
) && IsConfExemptLimits(aconf
))
713 rb_strlcpy(prefix_ptr
, name
, USERLEN
+ 1);
714 return (prefix_of_host
);
719 * Inputs: pointer to client to report to
721 * Side effects: Reports configured auth{} blocks to client_p
724 report_auth(struct Client
*client_p
)
726 char *name
, *host
, *user
, *classname
, *desc
;
728 struct AddressRec
*arec
;
729 struct ConfItem
*aconf
;
732 for (i
= 0; i
< ATABLE_SIZE
; i
++)
733 for (arec
= atable
[i
]; arec
; arec
= arec
->next
)
734 if(arec
->type
== CONF_CLIENT
)
738 if(!IsOperGeneral(client_p
) && IsConfDoSpoofIp(aconf
))
741 get_printable_conf(aconf
, &name
, &host
, &pass
, &user
, &port
,
744 if(!EmptyString(aconf
->spasswd
))
745 pass
= aconf
->spasswd
;
747 sendto_one_numeric(client_p
, RPL_STATSILINE
,
748 form_str(RPL_STATSILINE
),
749 name
, pass
, show_iline_prefix(client_p
, aconf
, user
),
750 show_ip_conf(aconf
, client_p
) ? host
: "255.255.255.255",
751 port
, classname
, desc
);