mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-07-09 22:23:13 +02:00
Added CIDR support
This commit is contained in:
@@ -3272,3 +3272,7 @@ This is the 3.2 fixes branch.
|
||||
- Win32: Readded /J compiler flag (was accidentally lost in December). This could cause
|
||||
some weird issues. Reported by Troco (#0001877).
|
||||
- Fixed compile problem with debugmode + ipv6
|
||||
- Added CIDR support (#0001296) suggested by many people.
|
||||
- Imported the latest CIDR functions from Hybrid 7.0.1 (and fixed numerous bugs)
|
||||
- Implemented CIDR support for ban ip, ban user, allow, except ban/tkl/throttle, and TKL
|
||||
- Note: This needs testing
|
||||
|
||||
+13
-2
@@ -64,7 +64,8 @@ English | <a href="unreal32docs.de.html">German</a><br><br>
|
||||
-- 3.12. <a href="#feature_antiflood">Anti-flood features</a><br>
|
||||
-- 3.13. <a href="#feature_bantypes">Ban types</a><br>
|
||||
-- 3.14. <a href="#feature_spamfilter">Spamfilter</a><br>
|
||||
-- 3.15. <a href="#feature_other">Other features</a><br>
|
||||
-- 3.15. <a href="#feature_cidr">CIDR</a><br>
|
||||
-- 3.16. <a href="#feature_other">Other features</a><br>
|
||||
4. <a href="#configuringyourunrealircdconf">Configuring your unrealircd.conf
|
||||
file</a><br>
|
||||
---4.1. <a href="#configurationfileexplained">Configuration file explained</a><br>
|
||||
@@ -509,7 +510,17 @@ into spaces at runtime. And double underscore ('__') gets an underscore ('_'). A
|
||||
<b>set::spamfilter::virus-help-channel-deny</b> allows you to block any normal joins to virus-help-channel (default: no)<br>
|
||||
</p></div>
|
||||
|
||||
<p><font size="+2"><b>3.15 - Other features</b></font><a name="feature_other"></a></p><div class="desc">
|
||||
<p><font size="+2"><b>3.15 - CIDR</b></font><a name="feature_cidr"></a></p><div class="desc">
|
||||
<p>UnrealIRCd now has support for CIDR (Classless Interdomain Routing). CIDR allows you to ban
|
||||
IP ranges. IPs are allocated to ISPs using CIDR, therefore, being able to set a CIDR based ban
|
||||
allows you to easily ban an ISP. Unreal supports CIDR for both IPv4 and IPv6. CIDR masks may be
|
||||
used in the allow::ip, ban user::mask, ban ip::mask, except ban::mask, except throttle::mask,
|
||||
and except tkl::mask (for gzline, gline, and shun). Additionally, CIDR can be used in /kline,
|
||||
/gline, /zline, /gzline, and /shun. Unreal uses the standard syntax of IP/bits, e.g.,
|
||||
127.0.0.0/8 (matches 127.0.0.0 - 127.255.255.255), and fe80:0:0:123::/64 (matches
|
||||
fe80:0:0:123:0:0:0:0 - fe80:0:0:123:ffff:ffff:ffff:ffff).</p></div>
|
||||
|
||||
<p><font size="+2"><b>3.16 - Other features</b></font><a name="feature_other"></a></p><div class="desc">
|
||||
<p>UnrealIRCd has a lot of features so not everything is covered here... You'll find that out by yourself.</p></div>
|
||||
|
||||
<p> </p>
|
||||
|
||||
+6
-8
@@ -122,21 +122,19 @@ ConfigItem_deny_dcc *Find_deny_dcc(char *name);
|
||||
ConfigItem_oper *Find_oper(char *name);
|
||||
ConfigItem_listen *Find_listen(char *ipmask, int port);
|
||||
ConfigItem_ulines *Find_uline(char *host);
|
||||
ConfigItem_except *Find_except(char *host, short type);
|
||||
ConfigItem_except *Find_except(aClient *, char *host, short type);
|
||||
ConfigItem_tld *Find_tld(aClient *cptr, char *host);
|
||||
ConfigItem_link *Find_link(char *username, char *hostname, char *ip, char *servername);
|
||||
ConfigItem_ban *Find_ban(char *host, short type);
|
||||
ConfigItem_ban *Find_banEx(char *host, short type, short type2);
|
||||
ConfigItem_ban *Find_ban(aClient *, char *host, short type);
|
||||
ConfigItem_ban *Find_banEx(aClient *,char *host, short type, short type2);
|
||||
ConfigItem_vhost *Find_vhost(char *name);
|
||||
ConfigItem_deny_channel *Find_channel_allowed(char *name);
|
||||
ConfigItem_alias *Find_alias(char *name);
|
||||
ConfigItem_help *Find_Help(char *command);
|
||||
int AllowClient(aClient *cptr, struct hostent *hp, char *sockhost, char *username);
|
||||
int parse_netmask(const char *text, struct IN_ADDR *addr, int *b);
|
||||
int match_ipv4(struct IN_ADDR *addr, struct IN_ADDR *mask, int b);
|
||||
#ifdef INET6
|
||||
int match_ipv6(struct IN_ADDR *addr, struct IN_ADDR *mask, int b);
|
||||
#endif
|
||||
int parse_netmask(const char *text, struct irc_netmask *netmask);
|
||||
int match_ip(struct IN_ADDR addr, char *uhost, char *mask, struct irc_netmask *netmask);
|
||||
ConfigItem_ban *Find_ban_ip(aClient *sptr);
|
||||
extern MODVAR struct tm motd_tm, smotd_tm;
|
||||
extern MODVAR Link *Servers;
|
||||
void add_ListItem(ListStruct *, ListStruct **);
|
||||
|
||||
+15
-5
@@ -658,6 +658,13 @@ typedef unsigned int u_int32_t; /* XXX Hope this works! */
|
||||
#define DCC_LINK_ME 1 /* My dcc allow */
|
||||
#define DCC_LINK_REMOTE 2 /* I need to remove dccallows from these clients when I die */
|
||||
|
||||
struct irc_netmask
|
||||
{
|
||||
short int type;
|
||||
struct IN_ADDR mask;
|
||||
short int bits;
|
||||
};
|
||||
|
||||
struct FloodOpt {
|
||||
unsigned short nmsg;
|
||||
TS firstmsg;
|
||||
@@ -790,7 +797,10 @@ struct t_kline {
|
||||
aTKline *prev, *next;
|
||||
int type;
|
||||
unsigned short subtype; /* subtype (currently spamfilter only), see SPAMF_* */
|
||||
Spamfilter *spamf;
|
||||
union {
|
||||
Spamfilter *spamf;
|
||||
struct irc_netmask *netmask;
|
||||
} ptr;
|
||||
char usermask[USERLEN + 3];
|
||||
char *hostmask, *reason, *setby;
|
||||
TS expire_at, set_at;
|
||||
@@ -1061,6 +1071,7 @@ struct _configitem_allow {
|
||||
unsigned short maxperip;
|
||||
int port;
|
||||
ConfigItem_class *class;
|
||||
struct irc_netmask *netmask;
|
||||
ConfigFlag_allow flags;
|
||||
};
|
||||
|
||||
@@ -1147,15 +1158,14 @@ struct _configitem_except {
|
||||
ConfigFlag_except flag;
|
||||
int type;
|
||||
char *mask;
|
||||
struct irc_netmask *netmask;
|
||||
};
|
||||
|
||||
struct _configitem_ban {
|
||||
ConfigItem *prev, *next;
|
||||
ConfigFlag_ban flag;
|
||||
char *mask, *reason;
|
||||
struct IN_ADDR netmask;
|
||||
int bits;
|
||||
short masktype;
|
||||
struct irc_netmask *netmask;
|
||||
unsigned short action;
|
||||
};
|
||||
|
||||
@@ -1697,7 +1707,7 @@ int hash_throttling(struct IN_ADDR *in);
|
||||
struct ThrottlingBucket *find_throttling_bucket(struct IN_ADDR *in);
|
||||
void add_throttling_bucket(struct IN_ADDR *in);
|
||||
void del_throttling_bucket(struct ThrottlingBucket *bucket);
|
||||
int throttle_can_connect(struct IN_ADDR *in);
|
||||
int throttle_can_connect(aClient *, struct IN_ADDR *in);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+307
-263
@@ -23,338 +23,382 @@
|
||||
#include "struct.h"
|
||||
#include "h.h"
|
||||
#include "inet.h"
|
||||
/* The following functions have been taken from Hybrid7-beta8 simply because
|
||||
/* The following functions have been taken from Hybrid-7.0.1 simply because
|
||||
* I didn't feel like writing my own when they had ones that work just fine :)
|
||||
* However, several bugs were found and some stuff was moved around to work
|
||||
* better.
|
||||
*/
|
||||
|
||||
#ifdef INET6
|
||||
static int parse_v6_netmask(const char *, struct IN_ADDR *, int *);
|
||||
static int parse_v6_netmask(const char *, struct IN_ADDR *addr, short int *b);
|
||||
#endif
|
||||
static int parse_v4_netmask(const char *, struct IN_ADDR *, int *);
|
||||
static int parse_v4_netmask(const char *, struct IN_ADDR *addr, short int *b);
|
||||
|
||||
#define DigitParse(ch) if (ch >= '0' && ch <= '9') \
|
||||
#define DigitParse(ch) do { \
|
||||
if (ch >= '0' && ch <= '9') \
|
||||
ch = ch - '0'; \
|
||||
else if (ch >= 'A' && ch <= 'F') \
|
||||
ch = ch - 'A' + '0'; \
|
||||
ch = ch - 'A' + 10; \
|
||||
else if (ch >= 'a' && ch <= 'f') \
|
||||
ch = ch - 'a' + '0';
|
||||
ch = ch - 'a' + 10; \
|
||||
} while(0);
|
||||
|
||||
/* The mask parser/type determination code... */
|
||||
|
||||
/* int parse_v6_netmask(const char *, struct IN_ADDR*, int *);
|
||||
/* int parse_v6_netmask(const char *, struct IN_ADDR*, short int *);
|
||||
* Input: An possible IPV6 address as a string.
|
||||
* Output: An integer describing whether it is an IPV6 or hostmask,
|
||||
* an address(if it is IPV6), a bitlength(if it is IPV6).
|
||||
* Side effects: None
|
||||
* Comments: Called from parse_netmask
|
||||
*/
|
||||
/* Fixed so ::/0 (any IPv6 address) is valid
|
||||
Also a bug in DigitParse above.
|
||||
-Gozem 2002-07-19 gozem@linux.nu
|
||||
*/
|
||||
#ifdef INET6
|
||||
static int
|
||||
parse_v6_netmask(const char *text, struct IN_ADDR *addr, int *b)
|
||||
static int parse_v6_netmask(const char *text, struct IN_ADDR *addr, short int *b)
|
||||
{
|
||||
const char *p;
|
||||
char c;
|
||||
int d[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }, dp = 0, nyble = 4, finsert =
|
||||
-1, bits = 0, deficit = 0;
|
||||
short dc[8];
|
||||
const char *p;
|
||||
char c;
|
||||
int d[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
int dp = 0;
|
||||
int nyble = 4;
|
||||
int finsert = -1;
|
||||
short int bits = 128;
|
||||
int deficit = 0;
|
||||
unsigned short dc[8];
|
||||
|
||||
for (p = text; (c = *p); p++)
|
||||
if (isxdigit(c))
|
||||
{
|
||||
if (nyble == 0)
|
||||
return HM_HOST;
|
||||
DigitParse(c);
|
||||
d[dp] |= c << (4 * --nyble);
|
||||
}
|
||||
else if (c == ':')
|
||||
{
|
||||
if (p > text && *(p - 1) == ':')
|
||||
{
|
||||
if (finsert >= 0)
|
||||
return HM_HOST;
|
||||
finsert = dp;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If there were less than 4 hex digits, e.g. :ABC: shift right
|
||||
* so we don't interpret it as ABC0 -A1kmm */
|
||||
d[dp] = d[dp] >> 4 * nyble;
|
||||
nyble = 4;
|
||||
if (++dp >= 8)
|
||||
return HM_HOST;
|
||||
}
|
||||
}
|
||||
else if (c == '*')
|
||||
{
|
||||
/* * must be last, and * is ambiguous if there is a ::... -A1kmm */
|
||||
if (finsert >= 0 || *(p + 1) || dp == 0 || *(p - 1) != ':')
|
||||
return HM_HOST;
|
||||
bits = dp * 16;
|
||||
}
|
||||
else if (c == '/')
|
||||
{
|
||||
char *after;
|
||||
for (p = text; (c = *p); p++)
|
||||
/* Parse a digit */
|
||||
if (isxdigit(c))
|
||||
{
|
||||
if (nyble == 0)
|
||||
return HM_HOST;
|
||||
DigitParse(c);
|
||||
d[dp] |= c << (4 * --nyble);
|
||||
}
|
||||
else if (c == ':')
|
||||
{
|
||||
/* It's a :: */
|
||||
if (p > text && *(p - 1) == ':')
|
||||
{
|
||||
if (finsert >= 0) /* Error: already has a :: */
|
||||
return HM_HOST;
|
||||
finsert = dp;
|
||||
}
|
||||
/* Just a regular : */
|
||||
else
|
||||
{
|
||||
/* If there were less than 4 hex digits, e.g. :ABC: shift right
|
||||
* so we don't interpret it as ABC0 -A1kmm */
|
||||
d[dp] = d[dp] >> 4 * nyble;
|
||||
nyble = 4;
|
||||
if (++dp >= 8) /* Error: more than 8 segments */
|
||||
return HM_HOST;
|
||||
}
|
||||
}
|
||||
/* Wildcard */
|
||||
else if (c == '*')
|
||||
{
|
||||
/* Error: there was a ::, or it is not the last segment */
|
||||
if (finsert >= 0 || *(p + 1) || dp == 0 || *(p - 1) != ':')
|
||||
return HM_HOST;
|
||||
bits = dp * 16;
|
||||
}
|
||||
/* Bit section */
|
||||
else if (c == '/')
|
||||
{
|
||||
char *after;
|
||||
|
||||
d[dp] = d[dp] >> 4 * nyble;
|
||||
dp++;
|
||||
if (p > text && *(p - 1) == ':')
|
||||
return HM_HOST;
|
||||
bits = strtoul(p + 1, &after, 10);
|
||||
if (bits == 0 || *after)
|
||||
return HM_HOST;
|
||||
if (bits > dp * 4 && !(finsert >= 0 && bits <= 128))
|
||||
return HM_HOST;
|
||||
break;
|
||||
}
|
||||
else
|
||||
return HM_HOST;
|
||||
d[dp] = d[dp] >> 4 * nyble;
|
||||
dp++;
|
||||
bits = strtoul(p + 1, &after, 10);
|
||||
if (bits < 0 || *after) /* Error: bits is invalid or not the end */
|
||||
return HM_HOST;
|
||||
/* Error: Bits is greater than the number of bits given
|
||||
* and there is no :: */
|
||||
if (bits > dp * 16 && !(finsert >= 0 && bits <= 128))
|
||||
return HM_HOST;
|
||||
break;
|
||||
}
|
||||
else /* Error: Illegal character */
|
||||
return HM_HOST;
|
||||
|
||||
d[dp] = d[dp] >> 4 * nyble;
|
||||
if (c == 0)
|
||||
dp++;
|
||||
if (finsert < 0 && bits == 0)
|
||||
bits = dp * 16;
|
||||
else if (bits == 0)
|
||||
bits = 128;
|
||||
/* How many words are missing? -A1kmm */
|
||||
deficit = bits / 16 + ((bits % 16) ? 1 : 0) - dp;
|
||||
/* Now fill in the gaps(from ::) in the copied table... -A1kmm */
|
||||
for (dp = 0, nyble = 0; dp < 8; dp++)
|
||||
{
|
||||
if (nyble == finsert && deficit)
|
||||
{
|
||||
dc[dp] = 0;
|
||||
deficit--;
|
||||
}
|
||||
else
|
||||
dc[dp] = d[nyble++];
|
||||
}
|
||||
/* Set unused bits to 0... -A1kmm */
|
||||
if (bits < 128 && (bits % 16 != 0))
|
||||
dc[bits / 16] &= ~((1 << (15 - bits % 16)) - 1);
|
||||
for (dp = bits / 16 + (bits % 16 ? 1 : 0); dp < 8; dp++)
|
||||
dc[dp] = 0;
|
||||
/* And assign... -A1kmm */
|
||||
if (addr)
|
||||
for (dp = 0; dp < 8; dp++)
|
||||
/* The cast is a kludge to make netbsd work. */
|
||||
((unsigned short *)&addr->s6_addr)[dp] = htons(dc[dp]);
|
||||
if (b)
|
||||
*b = bits;
|
||||
return HM_IPV6;
|
||||
/* This is handled above if it was a / */
|
||||
if (c != '/')
|
||||
d[dp] = d[dp] >> 4 * nyble;
|
||||
if (c == 0)
|
||||
dp++;
|
||||
|
||||
/* If there was no bit section, set the number of bits */
|
||||
if (finsert < 0 && bits == 0)
|
||||
bits = dp * 16;
|
||||
/* How many words are missing? -A1kmm */
|
||||
/* The original check was wrong -- codemastr */
|
||||
deficit = 8 - dp;
|
||||
|
||||
/* Now fill in the gaps(from ::) in the copied table... -A1kmm */
|
||||
for (dp = 0, nyble = 0; dp < 8; dp++)
|
||||
{
|
||||
if (nyble == finsert && deficit)
|
||||
{
|
||||
dc[dp] = 0;
|
||||
deficit--;
|
||||
}
|
||||
else
|
||||
dc[dp] = d[nyble++];
|
||||
}
|
||||
/* Set unused bits to 0... -A1kmm */
|
||||
/* This check was wrong as well -- codemastr */
|
||||
if (bits < 128 && (bits % 16 != 0))
|
||||
dc[bits / 16] &= ~((1 << (16 - bits % 16)) - 1);
|
||||
for (dp = bits / 16 + (bits % 16 ? 1 : 0); dp < 8; dp++)
|
||||
dc[dp] = 0;
|
||||
/* And assign... -A1kmm */
|
||||
if (addr)
|
||||
for (dp = 0; dp < 8; dp++)
|
||||
/* The cast is a kludge to make netbsd work. */
|
||||
((unsigned short *)&addr->s6_addr)[dp] = htons(dc[dp]);
|
||||
if (b != NULL)
|
||||
*b = bits;
|
||||
return HM_IPV6;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* int parse_v4_netmask(const char *, struct IN_ADDR *, int *);
|
||||
* Input: An possible IPV4 address as a string.
|
||||
/* int parse_v4_netmask(const char *, struct IN_ADDR *, short int *);
|
||||
* Input: A possible IPV4 address as a string.
|
||||
* Output: An integer describing whether it is an IPV4 or hostmask,
|
||||
* an address(if it is IPV4), a bitlength(if it is IPV4).
|
||||
* Side effects: None
|
||||
* Comments: Called from parse_netmask
|
||||
*/
|
||||
static int
|
||||
parse_v4_netmask(const char *text, struct IN_ADDR *addr, int *b)
|
||||
static int parse_v4_netmask(const char *text, struct IN_ADDR *addr, short int *b)
|
||||
{
|
||||
const char *p;
|
||||
const char *digits[4];
|
||||
unsigned char addb[4];
|
||||
int n = 0, bits = 0;
|
||||
char c;
|
||||
|
||||
digits[n++] = text;
|
||||
|
||||
for (p = text; (c = *p); p++)
|
||||
if (c >= '0' && c <= '9') /* empty */
|
||||
;
|
||||
else if (c == '.')
|
||||
{
|
||||
if (n >= 4) /* Error: More than four sections */
|
||||
return HM_HOST;
|
||||
digits[n++] = p + 1;
|
||||
}
|
||||
else if (c == '*')
|
||||
{
|
||||
if (*(p + 1) || n == 0 || *(p - 1) != '.') /* Error: * is not at the end
|
||||
* or not its own section */
|
||||
return HM_HOST;
|
||||
bits = (n - 1) * 8;
|
||||
break;
|
||||
}
|
||||
else if (c == '/')
|
||||
{
|
||||
char *after;
|
||||
bits = strtoul(p + 1, &after, 10);
|
||||
if (!bits || *after) /* Error: Invalid number or not end */
|
||||
return HM_HOST;
|
||||
if (bits > n * 8) /* Error: More than the bits given */
|
||||
return HM_HOST;
|
||||
break;
|
||||
}
|
||||
else /* Error: Illegal character */
|
||||
return HM_HOST;
|
||||
|
||||
if (n < 4 && bits == 0)
|
||||
bits = n * 8;
|
||||
if (bits)
|
||||
while (n < 4)
|
||||
digits[n++] = "0";
|
||||
for (n = 0; n < 4; n++)
|
||||
addb[n] = strtoul(digits[n], NULL, 10);
|
||||
if (bits == 0)
|
||||
bits = 32;
|
||||
/* Set unused bits to 0... -A1kmm */
|
||||
if (bits < 32 && bits % 8)
|
||||
addb[bits / 8] &= ~((1 << (8 - bits % 8)) - 1);
|
||||
for (n = bits / 8 + (bits % 8 ? 1 : 0); n < 4; n++)
|
||||
addb[n] = 0;
|
||||
if (addr)
|
||||
{
|
||||
#ifndef INET6
|
||||
const char *p;
|
||||
const char *digits[4];
|
||||
unsigned char addb[8]; /* will only use 4, but space for overflow [?]. -- Syzop*/
|
||||
int n = 0, bits = 0;
|
||||
char c;
|
||||
|
||||
digits[n++] = text;
|
||||
|
||||
for (p = text; (c = *p); p++)
|
||||
if (c >= '0' && c <= '9') /* empty */
|
||||
;
|
||||
else if (c == '.')
|
||||
{
|
||||
if (n >= 4)
|
||||
return HM_HOST;
|
||||
digits[n++] = p + 1;
|
||||
}
|
||||
else if (c == '*')
|
||||
{
|
||||
if (*(p + 1) || n == 0 || *(p - 1) != '.')
|
||||
return HM_HOST;
|
||||
bits = (n - 1) * 8;
|
||||
break;
|
||||
}
|
||||
else if (c == '/')
|
||||
{
|
||||
char *after;
|
||||
bits = strtoul(p + 1, &after, 10);
|
||||
if (!bits || *after)
|
||||
return HM_HOST;
|
||||
if (bits > n * 8)
|
||||
return HM_HOST;
|
||||
break;
|
||||
}
|
||||
else
|
||||
return HM_HOST;
|
||||
|
||||
if (n < 4 && bits == 0)
|
||||
bits = n * 8;
|
||||
if (bits)
|
||||
while (n < 4)
|
||||
digits[n++] = "0";
|
||||
for (n = 0; n < 4; n++)
|
||||
addb[n] = strtoul(digits[n], NULL, 10);
|
||||
if (bits == 0)
|
||||
bits = 32;
|
||||
/* Set unused bits to 0... -A1kmm */
|
||||
if (bits < 32 && bits % 8)
|
||||
addb[bits / 8] &= ~((1 << (8 - bits % 8)) - 1);
|
||||
for (n = bits / 8 + (bits % 8 ? 1 : 0); n < 8; n++)
|
||||
addb[n] = 0;
|
||||
if (addr)
|
||||
addr->S_ADDR =
|
||||
htonl(addb[0] << 24 | addb[1] << 16 | addb[2] << 8 | addb[3]);
|
||||
if (b)
|
||||
*b = bits;
|
||||
return HM_IPV4;
|
||||
addr->s_addr = htonl(addb[0] << 24 | addb[1] << 16 | addb[2] << 8 | addb[3]);
|
||||
#else
|
||||
u_char *cp;
|
||||
const char *p;
|
||||
const char *digits[4];
|
||||
unsigned char addb[4];
|
||||
int n = 0, bits = 0;
|
||||
char c;
|
||||
|
||||
digits[n++] = text;
|
||||
|
||||
for (p = text; (c = *p); p++)
|
||||
if (c >= '0' && c <= '9') /* empty */
|
||||
;
|
||||
else if (c == '.')
|
||||
{
|
||||
if (n >= 4)
|
||||
return HM_HOST;
|
||||
digits[n++] = p + 1;
|
||||
}
|
||||
else if (c == '*')
|
||||
{
|
||||
if (*(p + 1) || n == 0 || *(p - 1) != '.')
|
||||
return HM_HOST;
|
||||
bits = (n - 1) * 8;
|
||||
break;
|
||||
}
|
||||
else if (c == '/')
|
||||
{
|
||||
char *after;
|
||||
bits = strtoul(p + 1, &after, 10);
|
||||
if (!bits || *after)
|
||||
return HM_HOST;
|
||||
if (bits > n * 8)
|
||||
return HM_HOST;
|
||||
break;
|
||||
}
|
||||
else
|
||||
return HM_HOST;
|
||||
|
||||
if (n < 4 && bits == 0)
|
||||
bits = n * 8;
|
||||
if (bits)
|
||||
while (n < 4)
|
||||
digits[n++] = "0";
|
||||
for (n = 0; n < 4; n++)
|
||||
addb[n] = strtoul(digits[n], NULL, 10);
|
||||
if (bits == 0)
|
||||
bits = 32;
|
||||
/* Set unused bits to 0... -A1kmm */
|
||||
if (bits < 32 && bits % 8)
|
||||
addb[bits / 8] &= ~((1 << (8 - bits % 8)) - 1);
|
||||
for (n = bits / 8 + (bits % 8 ? 1 : 0); n < 8; n++)
|
||||
addb[n] = 0;
|
||||
if (addr)
|
||||
{
|
||||
cp = (u_char *)addr->s6_addr;
|
||||
for (n = 0; n <= 9; n++)
|
||||
cp[n] = 0;
|
||||
cp[10] = 0xff;
|
||||
cp[11] = 0xff;
|
||||
cp[12] = addb[0];
|
||||
cp[13] = addb[1];
|
||||
cp[14] = addb[2];
|
||||
cp[15] = addb[3];
|
||||
}
|
||||
if (b)
|
||||
*b = bits;
|
||||
return HM_IPV4;
|
||||
|
||||
for (n = 0; n <= 9; n++)
|
||||
addr->s6_addr[n] = 0;
|
||||
addr->s6_addr[10] = 0xff;
|
||||
addr->s6_addr[11] = 0xff;
|
||||
addr->s6_addr[12] = addb[0];
|
||||
addr->s6_addr[13] = addb[1];
|
||||
addr->s6_addr[14] = addb[2];
|
||||
addr->s6_addr[15] = addb[3];
|
||||
#endif
|
||||
}
|
||||
if (b)
|
||||
*b = bits;
|
||||
return HM_IPV4;
|
||||
}
|
||||
|
||||
/* int parse_netmask(const char *, struct IN_ADDR *, int *);
|
||||
/* int parse_netmask(const char *, struct irc_netmask *);
|
||||
* Input: A hostmask, or an IPV4/6 address.
|
||||
* Output: An integer describing whether it is an IPV4, IPV6 address or a
|
||||
* hostmask, an address(if it is an IP mask),
|
||||
* a bitlength(if it is IP mask).
|
||||
* Side effects: None
|
||||
*/
|
||||
int
|
||||
parse_netmask(const char *text, struct IN_ADDR *addr, int *b)
|
||||
int parse_netmask(const char *text, struct irc_netmask *netmask)
|
||||
{
|
||||
char *c;
|
||||
const char *host;
|
||||
|
||||
/* So a user@ip can be specified -- codemastr */
|
||||
if ((c = strchr(text, '@')) && *(c+1))
|
||||
host = c+1;
|
||||
else
|
||||
host = text;
|
||||
#ifdef INET6
|
||||
if (strchr(text, ':'))
|
||||
return parse_v6_netmask(text, addr, b);
|
||||
if (strchr(host, ':'))
|
||||
return parse_v6_netmask(host, &netmask->mask, &netmask->bits);
|
||||
#endif
|
||||
if (strchr(text, '.'))
|
||||
return parse_v4_netmask(text, addr, b);
|
||||
return HM_HOST;
|
||||
else if (strchr(host, '.'))
|
||||
return parse_v4_netmask(host, &netmask->mask, &netmask->bits);
|
||||
else
|
||||
{
|
||||
/* Well, lets just try and see?
|
||||
* This is here because ffff/10, for example,
|
||||
* is valid for our purposes */
|
||||
if (parse_v4_netmask(host, &netmask->mask, &netmask->bits) == HM_IPV4)
|
||||
return HM_IPV4;
|
||||
#ifdef INET6
|
||||
return parse_v6_netmask(host, &netmask->mask, &netmask->bits);
|
||||
#endif
|
||||
}
|
||||
return HM_HOST;
|
||||
}
|
||||
|
||||
/* The address matching stuff... */
|
||||
/* int match_ipv6(struct IN_ADDR *, struct IN_ADDR *, int)
|
||||
* Input: An IP address, an IP mask, the number of bits in the mask.
|
||||
* Output: if match, 0 else 1
|
||||
* Output: if match, 1 else 0
|
||||
* Side effects: None
|
||||
*/
|
||||
#ifdef INET6
|
||||
int
|
||||
match_ipv6(struct IN_ADDR *addr, struct IN_ADDR *mask, int bits)
|
||||
int match_ipv6(struct IN_ADDR *addr, struct IN_ADDR *mask, int bits)
|
||||
{
|
||||
int i, m, n = bits / 8;
|
||||
int i, m, n = bits / 8;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
if (addr->S_ADDR[i] != mask->S_ADDR[i])
|
||||
return 1;
|
||||
if ((m = bits % 8) == 0)
|
||||
return 0;
|
||||
if ((addr->S_ADDR[n] & ~((1 << (8 - m)) - 1)) ==
|
||||
mask->S_ADDR[n])
|
||||
return 0;
|
||||
return 1;
|
||||
for (i = 0; i < n; i++)
|
||||
if (addr->s6_addr[i] != mask->s6_addr[i])
|
||||
return 0;
|
||||
if ((m = bits % 8) == 0)
|
||||
return 1;
|
||||
if ((addr->s6_addr[n] & ~((1 << (8 - m)) - 1)) == mask->s6_addr[n])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* int match_ipv4(struct IN_ADDR *, struct IN_ADDR *, int)
|
||||
* Input: An IP address, an IP mask, the number of bits in the mask.
|
||||
* Output: if match, 0 else 1
|
||||
* Output: if match, 1 else 0
|
||||
* Side Effects: None
|
||||
*/
|
||||
int
|
||||
match_ipv4(struct IN_ADDR *addr, struct IN_ADDR *mask, int bits)
|
||||
int match_ipv4(struct IN_ADDR *addr, struct IN_ADDR *mask, int bits)
|
||||
{
|
||||
#ifndef INET6
|
||||
if ((ntohl(addr->S_ADDR) & ~((1 << (32 - bits)) - 1)) !=
|
||||
ntohl(mask->S_ADDR))
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
if ((ntohl(addr->s_addr) & ~((1 << (32 - bits)) - 1)) == ntohl(mask->s_addr))
|
||||
return 0;
|
||||
return 1;
|
||||
#else
|
||||
struct in_addr ipv4addr, ipv4mask;
|
||||
|
||||
ipv4addr.s_addr = inet_addr((char *)Inet_ia2p(addr));
|
||||
ipv4mask.s_addr = inet_addr((char *)Inet_ia2p(mask));
|
||||
if ((ntohl(ipv4addr.s_addr) & ~((1 << (32 - bits)) - 1)) !=
|
||||
ntohl(ipv4mask.s_addr))
|
||||
struct in_addr ipv4addr, ipv4mask;
|
||||
u_char *cp;
|
||||
cp = (u_char *)((struct IN_ADDR *)addr)->s6_addr;
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
|
||||
/* Make sure the address is IPv4 */
|
||||
if (cp[0] == 0 && cp[1] == 0 && cp[2] == 0 && cp[3] == 0 && cp[4] == 0
|
||||
&& cp[5] == 0 && cp[6] == 0 && cp[7] == 0 && cp[8] == 0
|
||||
&& cp[9] == 0 && cp[10] == 0xff && cp[11] == 0xff)
|
||||
{
|
||||
/* Convert the v6 representation to v4 */
|
||||
bcopy(&addr->s6_addr[12], &ipv4addr, sizeof(struct in_addr));
|
||||
bcopy(&mask->s6_addr[12], &ipv4mask, sizeof(struct in_addr));
|
||||
if ((ntohl(ipv4addr.s_addr) & ~((1 << (32 - bits)) - 1)) ==
|
||||
ntohl(ipv4mask.s_addr))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* int match_ip(aClient *sptr, int type, char *mask, struct irc_netmask netmask)
|
||||
* Input: a Client, a netmask type, a string mask, and a netmask struct
|
||||
* Output: if match, 1 else 0
|
||||
* Side Effects: None
|
||||
*/
|
||||
#if 0
|
||||
int match_ip(struct IN_ADDR addr, char *uhost, char *mask, struct irc_netmask *netmask)
|
||||
{
|
||||
if (!netmask)
|
||||
return (!match(mask, uhost));
|
||||
|
||||
switch (netmask->type)
|
||||
{
|
||||
case HM_HOST:
|
||||
return (!match(mask, uhost));
|
||||
case HM_IPV4:
|
||||
return match_ipv4(&addr, &netmask->mask, netmask->bits);
|
||||
#ifdef INET6
|
||||
case HM_IPV6:
|
||||
return match_ipv6(&addr, &netmask->mask, netmask->bits);
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
int match_ip(struct IN_ADDR addr, char *uhost, char *mask, struct irc_netmask *netmask)
|
||||
{
|
||||
char *end;
|
||||
|
||||
if (!netmask)
|
||||
return (!match(mask, uhost));
|
||||
|
||||
/* If it is an IP mask, we need to extract the user portion of both
|
||||
* and run a match.
|
||||
*/
|
||||
if (mask && (end = strchr(mask, '@')))
|
||||
{
|
||||
char username[USERLEN+1], usermask[USERLEN+1];
|
||||
strlcpy(usermask, mask, end-mask+1 > USERLEN+1 ? USERLEN+1 : end-mask+1);
|
||||
if ((end = strchr(uhost, '@')))
|
||||
{
|
||||
strlcpy(username, uhost, end-uhost+1 > USERLEN+1 ? USERLEN+1 : end-uhost+1);
|
||||
if (match(usermask, username))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
switch (netmask->type)
|
||||
{
|
||||
case HM_HOST:
|
||||
return (!match(mask, uhost));
|
||||
case HM_IPV4:
|
||||
return match_ipv4(&addr, &netmask->mask, netmask->bits);
|
||||
#ifdef INET6
|
||||
case HM_IPV6:
|
||||
return match_ipv6(&addr, &netmask->mask, netmask->bits);
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -846,7 +846,7 @@ void del_throttling_bucket(struct ThrottlingBucket *bucket)
|
||||
* @retval 2 Allowed, not in list or is an exception.
|
||||
* @see add_connection()
|
||||
*/
|
||||
int throttle_can_connect(struct IN_ADDR *in)
|
||||
int throttle_can_connect(aClient *sptr, struct IN_ADDR *in)
|
||||
{
|
||||
struct ThrottlingBucket *b;
|
||||
|
||||
@@ -857,7 +857,7 @@ int throttle_can_connect(struct IN_ADDR *in)
|
||||
return 1;
|
||||
else
|
||||
{
|
||||
if (Find_except(Inet_ia2p(in), CONF_EXCEPT_THROTTLE))
|
||||
if (Find_except(sptr, Inet_ia2p(in), CONF_EXCEPT_THROTTLE))
|
||||
return 2;
|
||||
b->count++;
|
||||
if (b->count > (THROTTLING_COUNT ? THROTTLING_COUNT : 3))
|
||||
|
||||
+3
-10
@@ -545,7 +545,7 @@ extern TS check_pings(TS currenttime)
|
||||
* If it's a user, we check for CONF_BAN_USER
|
||||
*/
|
||||
bconf =
|
||||
Find_ban(make_user_host(cptr->
|
||||
Find_ban(cptr, make_user_host(cptr->
|
||||
user ? cptr->user->username : cptr->
|
||||
username,
|
||||
cptr->user ? cptr->user->realhost : cptr->
|
||||
@@ -555,7 +555,7 @@ extern TS check_pings(TS currenttime)
|
||||
|
||||
if (!killflag && !IsAnOper(cptr) &&
|
||||
(bconf =
|
||||
Find_ban(cptr->info, CONF_BAN_REALNAME))) {
|
||||
Find_ban(NULL, cptr->info, CONF_BAN_REALNAME))) {
|
||||
killflag++;
|
||||
}
|
||||
|
||||
@@ -565,7 +565,7 @@ extern TS check_pings(TS currenttime)
|
||||
*/
|
||||
if (!killflag)
|
||||
if ((bconf =
|
||||
Find_ban(Inet_ia2p(&cptr->ip),
|
||||
Find_ban(cptr, Inet_ia2p(&cptr->ip),
|
||||
CONF_BAN_IP)))
|
||||
killflag++;
|
||||
if (killflag) {
|
||||
@@ -1128,13 +1128,6 @@ int InitwIRCD(int argc, char *argv[])
|
||||
}
|
||||
#endif
|
||||
exit(0);
|
||||
case 'W':{
|
||||
struct IN_ADDR bah;
|
||||
int bit;
|
||||
parse_netmask("255.255.255.255/8", &bah, &bit);
|
||||
printf("%s - %d\n", Inet_ia2p(&bah), bit);
|
||||
exit(0);
|
||||
}
|
||||
case 'C':
|
||||
config_verbose = atoi(p);
|
||||
break;
|
||||
|
||||
@@ -163,7 +163,7 @@ DLLFUNC int m_chgname(aClient *cptr, aClient *sptr, int parc, char *parv[])
|
||||
/* set the realname first to make n:line checking work */
|
||||
ircsprintf(acptr->info, "%s", parv[2]);
|
||||
/* only check for n:lines if the person who's name is being changed is not an oper */
|
||||
if (!IsAnOper(acptr) && Find_ban(acptr->info, CONF_BAN_REALNAME)) {
|
||||
if (!IsAnOper(acptr) && Find_ban(NULL, acptr->info, CONF_BAN_REALNAME)) {
|
||||
int xx;
|
||||
xx =
|
||||
exit_client(cptr, sptr, &me,
|
||||
|
||||
@@ -268,7 +268,7 @@ nohostcheck:
|
||||
return exit_client(acptr, acptr, acptr,
|
||||
"Server Exists");
|
||||
}
|
||||
if ((bconf = Find_ban(servername, CONF_BAN_SERVER)))
|
||||
if ((bconf = Find_ban(NULL, servername, CONF_BAN_SERVER)))
|
||||
{
|
||||
sendto_realops
|
||||
("Cancelling link %s, banned server",
|
||||
@@ -471,7 +471,7 @@ CMD_FUNC(m_server_remote)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if ((bconf = Find_ban(servername, CONF_BAN_SERVER)))
|
||||
if ((bconf = Find_ban(NULL, servername, CONF_BAN_SERVER)))
|
||||
{
|
||||
sendto_realops
|
||||
("Cancelling link %s, banned server %s",
|
||||
|
||||
@@ -116,7 +116,7 @@ DLLFUNC int m_setname(aClient *cptr, aClient *sptr, int parc, char *parv[]) {
|
||||
else
|
||||
ircsprintf(sptr->info, "%s", parv[1]);
|
||||
/* Check for n:lines here too */
|
||||
if (!IsAnOper(sptr) && Find_ban(sptr->info, CONF_BAN_REALNAME))
|
||||
if (!IsAnOper(sptr) && Find_ban(NULL, sptr->info, CONF_BAN_REALNAME))
|
||||
{
|
||||
int xx;
|
||||
xx =
|
||||
|
||||
@@ -137,7 +137,7 @@ DLLFUNC int m_svsnline(aClient *cptr, aClient *sptr, int parc, char *parv[])
|
||||
if (parc < 4)
|
||||
return 0;
|
||||
|
||||
if (!Find_banEx(parv[3], CONF_BAN_REALNAME, CONF_BAN_TYPE_AKILL))
|
||||
if (!Find_banEx(NULL, parv[3], CONF_BAN_REALNAME, CONF_BAN_TYPE_AKILL))
|
||||
{
|
||||
bconf = (ConfigItem_ban *) MyMallocEx(sizeof(ConfigItem_ban));
|
||||
bconf->flag.type = CONF_BAN_REALNAME;
|
||||
|
||||
+2
-2
@@ -1252,7 +1252,7 @@ add_con_refuse:
|
||||
}
|
||||
}
|
||||
|
||||
if ((bconf = Find_ban(acptr->sockhost, CONF_BAN_IP))) {
|
||||
if ((bconf = Find_ban(acptr, Inet_ia2p(&acptr->ip), CONF_BAN_IP))) {
|
||||
if (bconf)
|
||||
{
|
||||
ircsprintf(zlinebuf,
|
||||
@@ -1278,7 +1278,7 @@ add_con_refuse:
|
||||
else
|
||||
{
|
||||
int val;
|
||||
if (!(val = throttle_can_connect(&acptr->ip)))
|
||||
if (!(val = throttle_can_connect(acptr, &acptr->ip)))
|
||||
{
|
||||
ircsprintf(zlinebuf,
|
||||
"ERROR :Closing Link: [%s] (Throttled: Reconnecting too fast) -"
|
||||
|
||||
+71
-23
@@ -1479,11 +1479,11 @@ char *encoded;
|
||||
{
|
||||
if (tk->type != TKL_SPAMF)
|
||||
continue; /* global entry or something else.. */
|
||||
if (!strcmp(tk->spamf->tkl_reason, "<internally added by ircd>"))
|
||||
if (!strcmp(tk->ptr.spamf->tkl_reason, "<internally added by ircd>"))
|
||||
{
|
||||
MyFree(tk->spamf->tkl_reason);
|
||||
tk->spamf->tkl_reason = strdup(encoded);
|
||||
tk->spamf->tkl_duration = SPAMFILTER_BAN_TIME;
|
||||
MyFree(tk->ptr.spamf->tkl_reason);
|
||||
tk->ptr.spamf->tkl_reason = strdup(encoded);
|
||||
tk->ptr.spamf->tkl_duration = SPAMFILTER_BAN_TIME;
|
||||
}
|
||||
/* This one is even more ugly, but our config crap is VERY confusing :[ */
|
||||
if (!tk->setby)
|
||||
@@ -1726,6 +1726,8 @@ void config_rehash()
|
||||
next = (ListStruct *)allow_ptr->next;
|
||||
ircfree(allow_ptr->ip);
|
||||
ircfree(allow_ptr->hostname);
|
||||
if (allow_ptr->netmask)
|
||||
MyFree(allow_ptr->netmask);
|
||||
Auth_DeleteAuthStruct(allow_ptr->auth);
|
||||
DelListItem(allow_ptr, conf_allow);
|
||||
MyFree(allow_ptr);
|
||||
@@ -1734,6 +1736,8 @@ void config_rehash()
|
||||
{
|
||||
next = (ListStruct *)except_ptr->next;
|
||||
ircfree(except_ptr->mask);
|
||||
if (except_ptr->netmask)
|
||||
MyFree(except_ptr->netmask);
|
||||
DelListItem(except_ptr, conf_except);
|
||||
MyFree(except_ptr);
|
||||
}
|
||||
@@ -1744,6 +1748,8 @@ void config_rehash()
|
||||
{
|
||||
ircfree(ban_ptr->mask);
|
||||
ircfree(ban_ptr->reason);
|
||||
if (ban_ptr->netmask)
|
||||
MyFree(ban_ptr->netmask);
|
||||
DelListItem(ban_ptr, conf_ban);
|
||||
MyFree(ban_ptr);
|
||||
}
|
||||
@@ -2319,7 +2325,7 @@ ConfigItem_ulines *Find_uline(char *host) {
|
||||
}
|
||||
|
||||
|
||||
ConfigItem_except *Find_except(char *host, short type) {
|
||||
ConfigItem_except *Find_except(aClient *sptr, char *host, short type) {
|
||||
ConfigItem_except *excepts;
|
||||
|
||||
if (!host)
|
||||
@@ -2327,8 +2333,10 @@ ConfigItem_except *Find_except(char *host, short type) {
|
||||
|
||||
for(excepts = conf_except; excepts; excepts =(ConfigItem_except *) excepts->next) {
|
||||
if (excepts->flag.type == type)
|
||||
if (!match(excepts->mask, host))
|
||||
{
|
||||
if (match_ip(sptr->ip, host, excepts->mask, excepts->netmask))
|
||||
return excepts;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -2375,7 +2383,7 @@ ConfigItem_link *Find_link(char *username,
|
||||
|
||||
}
|
||||
|
||||
ConfigItem_ban *Find_ban(char *host, short type)
|
||||
ConfigItem_ban *Find_ban(aClient *sptr, char *host, short type)
|
||||
{
|
||||
ConfigItem_ban *ban;
|
||||
|
||||
@@ -2386,17 +2394,18 @@ ConfigItem_ban *Find_ban(char *host, short type)
|
||||
|
||||
for (ban = conf_ban; ban; ban = (ConfigItem_ban *) ban->next)
|
||||
if (ban->flag.type == type)
|
||||
if (!match(ban->mask, host)) {
|
||||
if (match_ip(sptr->ip, host, ban->mask, ban->netmask))
|
||||
{
|
||||
/* Person got a exception */
|
||||
if ((type == CONF_BAN_USER || type == CONF_BAN_IP)
|
||||
&& Find_except(host, CONF_EXCEPT_BAN))
|
||||
&& Find_except(sptr, host, CONF_EXCEPT_BAN))
|
||||
return NULL;
|
||||
return ban;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ConfigItem_ban *Find_banEx(char *host, short type, short type2)
|
||||
ConfigItem_ban *Find_banEx(aClient *sptr, char *host, short type, short type2)
|
||||
{
|
||||
ConfigItem_ban *ban;
|
||||
|
||||
@@ -2407,9 +2416,9 @@ ConfigItem_ban *Find_banEx(char *host, short type, short type2)
|
||||
|
||||
for (ban = conf_ban; ban; ban = (ConfigItem_ban *) ban->next)
|
||||
if ((ban->flag.type == type) && (ban->flag.type2 == type2))
|
||||
if (!match(ban->mask, host)) {
|
||||
if (match_ip(sptr->ip, host, ban->mask, ban->netmask)) {
|
||||
/* Person got a exception */
|
||||
if (Find_except(host, type))
|
||||
if (Find_except(sptr, host, type))
|
||||
return NULL;
|
||||
return ban;
|
||||
}
|
||||
@@ -2479,7 +2488,8 @@ int AllowClient(aClient *cptr, struct hostent *hp, char *sockhost, char *usernam
|
||||
else
|
||||
*uhost = '\0';
|
||||
(void)strncat(uhost, sockhost, sizeof(uhost) - strlen(uhost));
|
||||
if (!match(aconf->ip, uhost))
|
||||
/* Check the IP */
|
||||
if (match_ip(cptr->ip, uhost, aconf->ip, aconf->netmask))
|
||||
goto attach;
|
||||
|
||||
/* Hmm, localhost is a special case, hp == NULL and sockhost contains
|
||||
@@ -3827,7 +3837,7 @@ int _conf_allow(ConfigFile *conf, ConfigEntry *ce)
|
||||
ConfigEntry *cep, *cepp;
|
||||
ConfigItem_allow *allow;
|
||||
Hook *h;
|
||||
|
||||
struct irc_netmask tmp;
|
||||
if (ce->ce_vardata)
|
||||
{
|
||||
if (!strcmp(ce->ce_vardata, "channel"))
|
||||
@@ -3846,10 +3856,17 @@ int _conf_allow(ConfigFile *conf, ConfigEntry *ce)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
allow = MyMallocEx(sizeof(ConfigItem_allow));
|
||||
cep = config_find_entry(ce->ce_entries, "ip");
|
||||
allow->ip = strdup(cep->ce_vardata);
|
||||
/* CIDR */
|
||||
tmp.type = parse_netmask(allow->ip, &tmp);
|
||||
if (tmp.type != HM_HOST)
|
||||
{
|
||||
allow->netmask = MyMallocEx(sizeof(struct irc_netmask));
|
||||
bcopy(&tmp, allow->netmask, sizeof(struct irc_netmask));
|
||||
}
|
||||
|
||||
cep = config_find_entry(ce->ce_entries, "hostname");
|
||||
allow->hostname = strdup(cep->ce_vardata);
|
||||
if ((cep = config_find_entry(ce->ce_entries, "password")))
|
||||
@@ -4218,6 +4235,7 @@ int _conf_except(ConfigFile *conf, ConfigEntry *ce)
|
||||
ConfigEntry *cep, *cep2, *cep3;
|
||||
ConfigItem_except *ca;
|
||||
Hook *h;
|
||||
struct irc_netmask tmp;
|
||||
|
||||
if (!strcmp(ce->ce_vardata, "ban")) {
|
||||
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
|
||||
@@ -4225,6 +4243,12 @@ int _conf_except(ConfigFile *conf, ConfigEntry *ce)
|
||||
if (!strcmp(cep->ce_varname, "mask")) {
|
||||
ca = MyMallocEx(sizeof(ConfigItem_except));
|
||||
ca->mask = strdup(cep->ce_vardata);
|
||||
tmp.type = parse_netmask(ca->mask, &tmp);
|
||||
if (tmp.type != HM_HOST)
|
||||
{
|
||||
ca->netmask = MyMallocEx(sizeof(struct irc_netmask));
|
||||
bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask));
|
||||
}
|
||||
ca->flag.type = CONF_EXCEPT_BAN;
|
||||
AddListItem(ca, conf_except);
|
||||
}
|
||||
@@ -4239,6 +4263,12 @@ int _conf_except(ConfigFile *conf, ConfigEntry *ce)
|
||||
if (!strcmp(cep->ce_varname, "mask")) {
|
||||
ca = MyMallocEx(sizeof(ConfigItem_except));
|
||||
ca->mask = strdup(cep->ce_vardata);
|
||||
tmp.type = parse_netmask(ca->mask, &tmp);
|
||||
if (tmp.type != HM_HOST)
|
||||
{
|
||||
ca->netmask = MyMallocEx(sizeof(struct irc_netmask));
|
||||
bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask));
|
||||
}
|
||||
ca->flag.type = CONF_EXCEPT_THROTTLE;
|
||||
AddListItem(ca, conf_except);
|
||||
}
|
||||
@@ -4266,6 +4296,15 @@ int _conf_except(ConfigFile *conf, ConfigEntry *ce)
|
||||
else
|
||||
{}
|
||||
|
||||
if (ca->type & TKL_KILL || ca->type & TKL_ZAP || ca->type & TKL_SHUN)
|
||||
{
|
||||
tmp.type = parse_netmask(ca->mask, &tmp);
|
||||
if (tmp.type != HM_HOST)
|
||||
{
|
||||
ca->netmask = MyMallocEx(sizeof(struct irc_netmask));
|
||||
bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask));
|
||||
}
|
||||
}
|
||||
ca->flag.type = CONF_EXCEPT_TKL;
|
||||
AddListItem(ca, conf_except);
|
||||
}
|
||||
@@ -4930,18 +4969,18 @@ int _conf_spamfilter(ConfigFile *conf, ConfigEntry *ce)
|
||||
nl->hostmask = strdup(cep->ce_vardata);
|
||||
nl->setby = BadPtr(me.name) ? NULL : strdup(me.name); /* Hmm! */
|
||||
|
||||
nl->spamf = unreal_buildspamfilter(word);
|
||||
nl->spamf->action = action;
|
||||
nl->ptr.spamf = unreal_buildspamfilter(word);
|
||||
nl->ptr.spamf->action = action;
|
||||
|
||||
if ((cep = config_find_entry(ce->ce_entries, "reason")))
|
||||
nl->spamf->tkl_reason = strdup(unreal_encodespace(cep->ce_vardata));
|
||||
nl->ptr.spamf->tkl_reason = strdup(unreal_encodespace(cep->ce_vardata));
|
||||
else
|
||||
nl->spamf->tkl_reason = strdup("<internally added by ircd>");
|
||||
nl->ptr.spamf->tkl_reason = strdup("<internally added by ircd>");
|
||||
|
||||
if ((cep = config_find_entry(ce->ce_entries, "ban-time")))
|
||||
nl->spamf->tkl_duration = config_checkval(cep->ce_vardata, CFG_TIME);
|
||||
nl->ptr.spamf->tkl_duration = config_checkval(cep->ce_vardata, CFG_TIME);
|
||||
else
|
||||
nl->spamf->tkl_duration = (SPAMFILTER_BAN_TIME ? SPAMFILTER_BAN_TIME : 86400);
|
||||
nl->ptr.spamf->tkl_duration = (SPAMFILTER_BAN_TIME ? SPAMFILTER_BAN_TIME : 86400);
|
||||
|
||||
AddListItem(nl, tklines[tkl_hash('f')]);
|
||||
return 1;
|
||||
@@ -5500,8 +5539,17 @@ int _conf_ban(ConfigFile *conf, ConfigEntry *ce)
|
||||
}
|
||||
cep = config_find_entry(ce->ce_entries, "mask");
|
||||
ca->mask = strdup(cep->ce_vardata);
|
||||
if (ca->flag.type == CONF_BAN_IP)
|
||||
ca->masktype = parse_netmask(ca->mask, &ca->netmask, &ca->bits);
|
||||
if (ca->flag.type == CONF_BAN_IP || ca->flag.type == CONF_BAN_USER)
|
||||
{
|
||||
struct irc_netmask tmp;
|
||||
tmp.type = parse_netmask(ca->mask, &tmp);
|
||||
if (tmp.type != HM_HOST)
|
||||
{
|
||||
ca->netmask = MyMallocEx(sizeof(struct irc_netmask));
|
||||
bcopy(&tmp, ca->netmask, sizeof(struct irc_netmask));
|
||||
}
|
||||
}
|
||||
|
||||
cep = config_find_entry(ce->ce_entries, "reason");
|
||||
ca->reason = strdup(cep->ce_vardata);
|
||||
cep = config_find_entry(ce->ce_entries, "action");
|
||||
|
||||
+100
-40
@@ -142,17 +142,26 @@ int tkl_add_line(int type, char *usermask, char *hostmask, char *reason, char *
|
||||
{
|
||||
/* Need to set some additional flags like 'targets' and 'action'.. */
|
||||
nl->subtype = spamfilter_gettargets(usermask, NULL);
|
||||
nl->spamf = unreal_buildspamfilter(reason);
|
||||
nl->spamf->action = banact_chartoval(*hostmask);
|
||||
nl->ptr.spamf = unreal_buildspamfilter(reason);
|
||||
nl->ptr.spamf->action = banact_chartoval(*hostmask);
|
||||
nl->expire_at = 0; /* temporary spamfilters are NOT supported! (makes no sense) */
|
||||
if (!spamf_tkl_reason)
|
||||
{
|
||||
/* no exttkl support, use default values... */
|
||||
nl->spamf->tkl_duration = SPAMFILTER_BAN_TIME;
|
||||
nl->spamf->tkl_reason = strdup(unreal_encodespace(SPAMFILTER_BAN_REASON));
|
||||
nl->ptr.spamf->tkl_duration = SPAMFILTER_BAN_TIME;
|
||||
nl->ptr.spamf->tkl_reason = strdup(unreal_encodespace(SPAMFILTER_BAN_REASON));
|
||||
} else {
|
||||
nl->spamf->tkl_duration = spamf_tkl_duration;
|
||||
nl->spamf->tkl_reason = strdup(spamf_tkl_reason); /* already encoded */
|
||||
nl->ptr.spamf->tkl_duration = spamf_tkl_duration;
|
||||
nl->ptr.spamf->tkl_reason = strdup(spamf_tkl_reason); /* already encoded */
|
||||
}
|
||||
}
|
||||
else if (type & TKL_KILL || type & TKL_ZAP || type & TKL_SHUN)
|
||||
{
|
||||
struct irc_netmask tmp;
|
||||
if ((tmp.type = parse_netmask(nl->hostmask, &tmp)) != HM_HOST)
|
||||
{
|
||||
nl->ptr.netmask = MyMallocEx(sizeof(struct irc_netmask));
|
||||
bcopy(&tmp, nl->ptr.netmask, sizeof(struct irc_netmask));
|
||||
}
|
||||
}
|
||||
index = tkl_hash(tkl_typetochar(type));
|
||||
@@ -173,13 +182,16 @@ aTKline *tkl_del_line(aTKline *tkl)
|
||||
MyFree(p->hostmask);
|
||||
MyFree(p->reason);
|
||||
MyFree(p->setby);
|
||||
if (p->spamf)
|
||||
if (p->type & TKL_SPAMF && p->ptr.spamf)
|
||||
{
|
||||
regfree(&p->spamf->expr);
|
||||
if (p->spamf->tkl_reason)
|
||||
MyFree(p->spamf->tkl_reason);
|
||||
MyFree(p->spamf);
|
||||
regfree(&p->ptr.spamf->expr);
|
||||
if (p->ptr.spamf->tkl_reason)
|
||||
MyFree(p->ptr.spamf->tkl_reason);
|
||||
MyFree(p->ptr.spamf);
|
||||
}
|
||||
if ((p->type & TKL_KILL || p->type & TKL_ZAP || p->type & TKL_SHUN)
|
||||
&& p->ptr.netmask)
|
||||
MyFree(p->ptr.netmask);
|
||||
DelListItem(p, tklines[index]);
|
||||
MyFree(p);
|
||||
return q;
|
||||
@@ -364,12 +376,28 @@ int find_tkline_match(aClient *cptr, int xx)
|
||||
{
|
||||
if ((lp->type & TKL_SHUN) || (lp->type & TKL_SPAMF) || (lp->type & TKL_NICK))
|
||||
continue;
|
||||
|
||||
/* If it's tangy and brown, you're in CIDR town! */
|
||||
if (lp->ptr.netmask)
|
||||
{
|
||||
if (match_ip(cptr->ip, NULL, NULL, lp->ptr.netmask) &&
|
||||
!match(lp->usermask, cname))
|
||||
{
|
||||
points = 1;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!match(lp->usermask, cname) && !match(lp->hostmask, chost))
|
||||
{
|
||||
points = 1;
|
||||
if (!match(lp->usermask, cname) && !match(lp->hostmask, cip))
|
||||
points = 1;
|
||||
if (points)
|
||||
break;
|
||||
}
|
||||
if (!match(lp->usermask, cname) && !match(lp->hostmask, cip))
|
||||
{
|
||||
points = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (points)
|
||||
break;
|
||||
@@ -387,7 +415,11 @@ int find_tkline_match(aClient *cptr, int xx)
|
||||
if (excepts->flag.type != match_type || (match_type == CONF_EXCEPT_TKL &&
|
||||
excepts->type != lp->type))
|
||||
continue;
|
||||
if (!match(excepts->mask, host) || !match(excepts->mask, host2))
|
||||
|
||||
if (excepts->netmask)
|
||||
if (match_ip(cptr->ip, NULL, NULL, excepts->netmask))
|
||||
return 1;
|
||||
else if (!match(excepts->mask, host) || !match(excepts->mask, host2))
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -468,12 +500,28 @@ int find_shun(aClient *cptr)
|
||||
if (!(lp->type & TKL_SHUN))
|
||||
continue;
|
||||
|
||||
/* CIDR */
|
||||
if (lp->ptr.netmask)
|
||||
{
|
||||
if (match_ip(cptr->ip, NULL, NULL, lp->ptr.netmask) &&
|
||||
!match(lp->usermask, cname))
|
||||
{
|
||||
points = 1;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!match(lp->usermask, cname) && !match(lp->hostmask, chost))
|
||||
{
|
||||
points = 1;
|
||||
if (!match(lp->usermask, cname) && !match(lp->hostmask, cip))
|
||||
points = 1;
|
||||
if (points == 1)
|
||||
break;
|
||||
}
|
||||
if (!match(lp->usermask, cname) && !match(lp->hostmask, cip))
|
||||
{
|
||||
points = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
points = 0;
|
||||
}
|
||||
@@ -483,11 +531,15 @@ int find_shun(aClient *cptr)
|
||||
strcpy(host, make_user_host(cname, chost));
|
||||
strcpy(host2, make_user_host(cname, cip));
|
||||
match_type = CONF_EXCEPT_TKL;
|
||||
|
||||
for (excepts = conf_except; excepts; excepts = (ConfigItem_except *)excepts->next) {
|
||||
if (excepts->flag.type != match_type || (match_type == CONF_EXCEPT_TKL &&
|
||||
excepts->type != lp->type))
|
||||
continue;
|
||||
if (!match(excepts->mask, host) || !match(excepts->mask, host2))
|
||||
if (excepts->netmask)
|
||||
if (match_ip(cptr->ip, NULL, NULL, excepts->netmask))
|
||||
return 1;
|
||||
else if (!match(excepts->mask, host) || !match(excepts->mask, host2))
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -538,7 +590,10 @@ aTKline *find_qline(aClient *cptr, char *nick, int *ishold)
|
||||
for (excepts = conf_except; excepts; excepts = (ConfigItem_except *)excepts->next) {
|
||||
if (excepts->flag.type != CONF_EXCEPT_TKL || excepts->type != TKL_NICK)
|
||||
continue;
|
||||
if (!match(excepts->mask, host) || !match(excepts->mask, host2))
|
||||
if (excepts->netmask)
|
||||
if (match_ip(cptr->ip, NULL, NULL, excepts->netmask))
|
||||
return 1;
|
||||
else if (!match(excepts->mask, host) || !match(excepts->mask, host2))
|
||||
return NULL;
|
||||
}
|
||||
return lp;
|
||||
@@ -564,12 +619,17 @@ int find_tkline_match_zap(aClient *cptr)
|
||||
{
|
||||
if (lp->type & TKL_ZAP)
|
||||
{
|
||||
if (!match(lp->hostmask, cip))
|
||||
if ((lp->ptr.netmask && match_ip(cptr->ip, NULL, NULL, lp->ptr.netmask))
|
||||
|| !match(lp->hostmask, cip))
|
||||
{
|
||||
|
||||
for (excepts = conf_except; excepts; excepts = (ConfigItem_except *)excepts->next) {
|
||||
if (excepts->flag.type != CONF_EXCEPT_TKL || excepts->type != lp->type)
|
||||
continue;
|
||||
if (!match(excepts->mask, cip))
|
||||
if (excepts->netmask)
|
||||
if (match_ip(cptr->ip, NULL, NULL, excepts->netmask))
|
||||
return -1;
|
||||
else if (!match(excepts->mask, cip))
|
||||
return -1;
|
||||
}
|
||||
for (tmphook = Hooks[HOOKTYPE_TKL_EXCEPT]; tmphook; tmphook = tmphook->next)
|
||||
@@ -763,10 +823,10 @@ void tkl_stats(aClient *cptr, int type, char *para)
|
||||
cptr->name,
|
||||
(tk->type & TKL_GLOBAL) ? 'F' : 'f',
|
||||
spamfilter_target_inttostring(tk->subtype),
|
||||
banact_valtostring(tk->spamf->action),
|
||||
banact_valtostring(tk->ptr.spamf->action),
|
||||
(tk->expire_at != 0) ? (tk->expire_at - curtime) : 0,
|
||||
curtime - tk->set_at,
|
||||
tk->spamf->tkl_duration, tk->spamf->tkl_reason,
|
||||
tk->ptr.spamf->tkl_duration, tk->ptr.spamf->tkl_reason,
|
||||
tk->setby,
|
||||
tk->reason);
|
||||
}
|
||||
@@ -808,7 +868,7 @@ void tkl_synch(aClient *sptr)
|
||||
typ,
|
||||
tk->usermask, tk->hostmask, tk->setby,
|
||||
tk->expire_at, tk->set_at,
|
||||
tk->spamf->tkl_duration, tk->spamf->tkl_reason,
|
||||
tk->ptr.spamf->tkl_duration, tk->ptr.spamf->tkl_reason,
|
||||
tk->reason);
|
||||
} else
|
||||
sendto_one(sptr,
|
||||
@@ -984,8 +1044,8 @@ int m_tkl(aClient *cptr, aClient *sptr, int parc, char *parv[])
|
||||
MSG_TKL, TOK_TKL,
|
||||
"%s %s %s %s %s %ld %ld %ld %s :%s",
|
||||
parv[1], parv[2], parv[3], parv[4],
|
||||
tk->setby, tk->expire_at, tk->set_at, tk->spamf->tkl_duration,
|
||||
tk->spamf->tkl_reason, tk->reason);
|
||||
tk->setby, tk->expire_at, tk->set_at, tk->ptr.spamf->tkl_duration,
|
||||
tk->ptr.spamf->tkl_reason, tk->reason);
|
||||
} else
|
||||
sendto_serv_butone(cptr,
|
||||
":%s TKL %s %s %s %s %s %ld %ld :%s", sptr->name,
|
||||
@@ -1372,7 +1432,7 @@ char *str = (char *)StripControlCodes(str_in);
|
||||
{
|
||||
if (!(tk->subtype & type))
|
||||
continue;
|
||||
if (!regexec(&tk->spamf->expr, str, 0, NULL, 0))
|
||||
if (!regexec(&tk->ptr.spamf->expr, str, 0, NULL, 0))
|
||||
{
|
||||
/* matched! */
|
||||
char buf[1024];
|
||||
@@ -1391,48 +1451,48 @@ char *str = (char *)StripControlCodes(str_in);
|
||||
sptr->name, sptr->user->username, sptr->user->realhost,
|
||||
tk->reason,
|
||||
spamfilter_inttostring_long(type), targetbuf, str,
|
||||
unreal_decodespace(tk->spamf->tkl_reason));
|
||||
unreal_decodespace(tk->ptr.spamf->tkl_reason));
|
||||
|
||||
sendto_snomask(SNO_SPAMF, "%s", buf);
|
||||
sendto_serv_butone_token(NULL, me.name, MSG_SENDSNO, TOK_SENDSNO, "S :%s", buf);
|
||||
|
||||
if (tk->spamf->action == BAN_ACT_BLOCK)
|
||||
if (tk->ptr.spamf->action == BAN_ACT_BLOCK)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case SPAMF_USERMSG:
|
||||
case SPAMF_USERNOTICE:
|
||||
sendnotice(sptr, "Message to %s blocked: %s",
|
||||
target, unreal_decodespace(tk->spamf->tkl_reason));
|
||||
target, unreal_decodespace(tk->ptr.spamf->tkl_reason));
|
||||
break;
|
||||
case SPAMF_CHANMSG:
|
||||
case SPAMF_CHANNOTICE:
|
||||
sendto_one(sptr, ":%s 404 %s %s :Message blocked: %s",
|
||||
me.name, sptr->name, target,
|
||||
unreal_decodespace(tk->spamf->tkl_reason));
|
||||
unreal_decodespace(tk->ptr.spamf->tkl_reason));
|
||||
break;
|
||||
case SPAMF_DCC:
|
||||
sendnotice(sptr, "DCC to %s blocked: %s",
|
||||
target, unreal_decodespace(tk->spamf->tkl_reason));
|
||||
target, unreal_decodespace(tk->ptr.spamf->tkl_reason));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
} else
|
||||
if (tk->spamf->action == BAN_ACT_DCCBLOCK)
|
||||
if (tk->ptr.spamf->action == BAN_ACT_DCCBLOCK)
|
||||
{
|
||||
if (type == SPAMF_DCC)
|
||||
{
|
||||
sendnotice(sptr, "DCC to %s blocked: %s",
|
||||
target, unreal_decodespace(tk->spamf->tkl_reason));
|
||||
target, unreal_decodespace(tk->ptr.spamf->tkl_reason));
|
||||
sendnotice(sptr, "*** You have been blocked from sending files, "
|
||||
"reconnect to regain permission to send files");
|
||||
sptr->flags |= FLAGS_DCCBLOCK;
|
||||
}
|
||||
return -1;
|
||||
} else
|
||||
if (tk->spamf->action == BAN_ACT_VIRUSCHAN)
|
||||
if (tk->ptr.spamf->action == BAN_ACT_VIRUSCHAN)
|
||||
{
|
||||
char *xparv[3], chbuf[CHANNELLEN + 16];
|
||||
aChannel *chptr;
|
||||
@@ -1451,7 +1511,7 @@ char *str = (char *)StripControlCodes(str_in);
|
||||
if (ret == FLUSH_BUFFER)
|
||||
return FLUSH_BUFFER; /* don't ask me how we could have died... */
|
||||
sendnotice(sptr, "You are now restricted to talking in %s: %s",
|
||||
SPAMFILTER_VIRUSCHAN, unreal_decodespace(tk->spamf->tkl_reason));
|
||||
SPAMFILTER_VIRUSCHAN, unreal_decodespace(tk->ptr.spamf->tkl_reason));
|
||||
/* todo: send notice to channel? */
|
||||
chptr = find_channel(SPAMFILTER_VIRUSCHAN, NULL);
|
||||
if (chptr)
|
||||
@@ -1459,15 +1519,15 @@ char *str = (char *)StripControlCodes(str_in);
|
||||
ircsprintf(chbuf, "@%s", chptr->chname);
|
||||
ircsprintf(buf, "[Spamfilter] %s matched filter '%s' [%s%s] [%s]",
|
||||
sptr->name, tk->reason, spamfilter_inttostring_long(type), targetbuf,
|
||||
unreal_decodespace(tk->spamf->tkl_reason));
|
||||
unreal_decodespace(tk->ptr.spamf->tkl_reason));
|
||||
sendto_channelprefix_butone_tok(NULL, &me, chptr, PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER,
|
||||
MSG_NOTICE, TOK_NOTICE, chbuf, buf, 0);
|
||||
}
|
||||
SetVirus(sptr);
|
||||
return -1;
|
||||
} else
|
||||
return place_host_ban(sptr, tk->spamf->action,
|
||||
unreal_decodespace(tk->spamf->tkl_reason), tk->spamf->tkl_duration);
|
||||
return place_host_ban(sptr, tk->ptr.spamf->action,
|
||||
unreal_decodespace(tk->ptr.spamf->tkl_reason), tk->ptr.spamf->tkl_duration);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
+2
-2
@@ -933,7 +933,7 @@ int register_user(aClient *cptr, aClient *sptr, char *nick, char *username, char
|
||||
* following block for the benefit of time-dependent K:-lines
|
||||
*/
|
||||
if ((bconf =
|
||||
Find_ban(make_user_host(user->username, user->realhost),
|
||||
Find_ban(sptr, make_user_host(user->username, user->realhost),
|
||||
CONF_BAN_USER)))
|
||||
{
|
||||
ircstp->is_ref++;
|
||||
@@ -945,7 +945,7 @@ int register_user(aClient *cptr, aClient *sptr, char *nick, char *username, char
|
||||
KLINE_ADDRESS);
|
||||
return exit_client(cptr, cptr, cptr, "You are banned");
|
||||
}
|
||||
if ((bconf = Find_ban(sptr->info, CONF_BAN_REALNAME)))
|
||||
if ((bconf = Find_ban(NULL, sptr->info, CONF_BAN_REALNAME)))
|
||||
{
|
||||
ircstp->is_ref++;
|
||||
sendto_one(cptr,
|
||||
|
||||
+1
-1
@@ -417,7 +417,7 @@ int ban_version(aClient *cptr, aClient *sptr, int parc, char *parv[])
|
||||
return 0;
|
||||
if (parv[1][len-1] == '\1')
|
||||
parv[1][len-1] = '\0';
|
||||
if ((ban = Find_ban(parv[1], CONF_BAN_VERSION)))
|
||||
if ((ban = Find_ban(NULL, parv[1], CONF_BAN_VERSION)))
|
||||
return place_host_ban(sptr, ban->action, ban->reason, BAN_VERSION_TKL_TIME);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user