diff --git a/include/dynconf.h b/include/dynconf.h index 47b11edf0..27572dc9c 100644 --- a/include/dynconf.h +++ b/include/dynconf.h @@ -160,6 +160,7 @@ struct Configuration { BanTarget manual_ban_target; char *reject_message_too_many_connections; char *reject_message_too_many_connections_ipv6_range; + char *reject_message_too_many_new_connections_ipv6_range; char *reject_message_server_full; char *reject_message_unauthorized; char *reject_message_kline; diff --git a/include/h.h b/include/h.h index a796829dc..0694f4f51 100644 --- a/include/h.h +++ b/include/h.h @@ -529,6 +529,7 @@ extern const char *inetntop(int af, const void *in, char *local_dummy, size_t th extern void mask_ipv6_rawip(const char *src, int prefix, char *dst); extern const char *get_clone_mask_ipstr(Client *client, char *buf, size_t buflen); +extern const char *format_ipv6_prefix_reject_message(const char *template, const char *masked_rawip, int prefix); extern void delletterfromstring(char *s, char letter); extern void addlettertodynamicstringsorted(char **str, char letter); diff --git a/src/conf.c b/src/conf.c index c11d27527..708491f3d 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1784,6 +1784,7 @@ void free_iConf(Configuration *i) safe_free(i->spamexcept_line); safe_free(i->reject_message_too_many_connections); safe_free(i->reject_message_too_many_connections_ipv6_range); + safe_free(i->reject_message_too_many_new_connections_ipv6_range); safe_free(i->reject_message_server_full); safe_free(i->reject_message_unauthorized); safe_free(i->reject_message_kline); @@ -1916,8 +1917,9 @@ void config_setdefaultsettings(Configuration *i) i->outdated_tls_policy_oper = POLICY_DENY; i->outdated_tls_policy_server = POLICY_DENY; - safe_strdup(i->reject_message_too_many_connections, "Too many connections from your IP"); - safe_strdup(i->reject_message_too_many_connections_ipv6_range, "Too many new connections from this IPv6 range ($prefix_addr/$prefix_len)"); + safe_strdup(i->reject_message_too_many_connections, "Too many connections from your IP [maxperip]"); + safe_strdup(i->reject_message_too_many_connections_ipv6_range, "Too many connections from your IPv6 range ($prefix_addr/$prefix_len) [maxperip]"); + safe_strdup(i->reject_message_too_many_new_connections_ipv6_range, "Too many new connections from this IPv6 range ($prefix_addr/$prefix_len) [connthrottle]"); safe_strdup(i->reject_message_server_full, "This server is full"); safe_strdup(i->reject_message_unauthorized, "You are not authorized to connect to this server"); safe_strdup(i->reject_message_kline, "You are not welcome on this server. $bantype: $banreason. Email $klineaddr for more information."); @@ -8399,6 +8401,8 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) safe_strdup(tempiConf.reject_message_too_many_connections, cepp->value); else if (!strcmp(cepp->name, "too-many-connections-ipv6-range")) safe_strdup(tempiConf.reject_message_too_many_connections_ipv6_range, cepp->value); + else if (!strcmp(cepp->name, "too-many-new-connections-ipv6-range")) + safe_strdup(tempiConf.reject_message_too_many_new_connections_ipv6_range, cepp->value); else if (!strcmp(cepp->name, "server-full")) safe_strdup(tempiConf.reject_message_server_full, cepp->value); else if (!strcmp(cepp->name, "unauthorized")) @@ -9916,6 +9920,10 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) ; else if (!strcmp(cepp->name, "too-many-connections")) ; + else if (!strcmp(cepp->name, "too-many-connections-ipv6-range")) + ; + else if (!strcmp(cepp->name, "too-many-new-connections-ipv6-range")) + ; else if (!strcmp(cepp->name, "server-full")) ; else if (!strcmp(cepp->name, "unauthorized")) diff --git a/src/match.c b/src/match.c index 6ed7afbb4..3eda94de8 100644 --- a/src/match.c +++ b/src/match.c @@ -939,3 +939,40 @@ const char *get_clone_mask_ipstr(Client *client, char *buf, size_t buflen) mask_ipv6_rawip(client->rawip, iConf.default_ipv6_clone_mask, masked); return inetntop(AF_INET6, masked, buf, buflen); } + +/** Format a user-facing reject message for an IPv6 prefix-aware rejection. + * + * Substitutes $prefix_addr (compressed IPv6 form, e.g. "2001:db8::") and + * $prefix_len (decimal) into the supplied template via buildvarstring. + * + * Used by both maxperip (for /N clone-cap rejections) and connthrottle + * (for /56/48/32 wider-prefix rejections). + * + * Returns a pointer to internal static storage, overwritten on each call. + * + * @param template The message template (typically iConf.reject_message_*). + * @param masked_rawip 16-byte masked raw IPv6 address. + * @param prefix Prefix length in bits. + * @return Formatted message in static buffer. + */ +const char *format_ipv6_prefix_reject_message(const char *template, + const char *masked_rawip, + int prefix) +{ + static char buf[512]; + char prefix_len_str[8]; + char addr_str[128]; /* generously oversized; longest IPv6 string form is ~46 chars */ + const char *vars[3], *values[3]; + + if (!inet_ntop(AF_INET6, masked_rawip, addr_str, sizeof(addr_str))) + strlcpy(addr_str, "?", sizeof(addr_str)); + ircsnprintf(prefix_len_str, sizeof(prefix_len_str), "%d", prefix); + vars[0] = "prefix_addr"; + values[0] = addr_str; + vars[1] = "prefix_len"; + values[1] = prefix_len_str; + vars[2] = NULL; + values[2] = NULL; + buildvarstring(template, buf, sizeof(buf), vars, values); + return buf; +} diff --git a/src/modules/connthrottle.c b/src/modules/connthrottle.c index eb928b910..e5d54d962 100644 --- a/src/modules/connthrottle.c +++ b/src/modules/connthrottle.c @@ -135,7 +135,6 @@ static void ct_bucket_bump_client(Client *client, ConnThrottleCategory category) static void ct_bucket_unbump_client(Client *client, ConnThrottleCategory category); static void ct_buckets_rebuild(void); static void ct_buckets_free(void); -static const char *ct_format_reject_reason(const char *masked, int prefix); static const char *ct_module_status_text(void); const char *ct_allow_client(Client *client, ConfigItem_allow *aconf); int ct_remote_connect_buckets(Client *client); @@ -1194,30 +1193,6 @@ static void ct_bucket_unbump_client(Client *client, ConnThrottleCategory categor } } -/* Build the user-facing rejection message. With $prefix_addr (compressed - * form, not the usual uncompressed form we use) and with $tier. - */ -static const char *ct_format_reject_reason(const char *masked, int prefix) -{ - static char buf[512]; - char prefix_len_str[8]; - char addr_str[INET6_ADDRSTRLEN]; - const char *vars[3], *values[3]; - - if (!inet_ntop(AF_INET6, masked, addr_str, sizeof(addr_str))) - strlcpy(addr_str, "?", sizeof(addr_str)); - ircsnprintf(prefix_len_str, sizeof(prefix_len_str), "%d", prefix); - vars[0] = "prefix_addr"; - values[0] = addr_str; - vars[1] = "prefix_len"; - values[1] = prefix_len_str; - vars[2] = NULL; - values[2] = NULL; - buildvarstring(iConf.reject_message_too_many_connections_ipv6_range, - buf, sizeof(buf), vars, values); - return buf; -} - /** HOOKTYPE_ALLOW_CLIENT: classify the client, add to buckets, and * reject if the unknown_users count for this client's category exceeds * the limit at any tier. Multiple matched allow blocks may invoke this @@ -1270,7 +1245,9 @@ const char *ct_allow_client(Client *client, ConfigItem_allow *aconf) ct_make_rawip(client, tier, masked); b = ct_find_bucket(tier, masked); if (b && (b->unknown_users > effective_limit)) - return ct_format_reject_reason(masked, ct_tier_prefix[tier]); + return format_ipv6_prefix_reject_message( + iConf.reject_message_too_many_new_connections_ipv6_range, + masked, ct_tier_prefix[tier]); } return NULL; diff --git a/src/modules/maxperip.c b/src/modules/maxperip.c index f80b7f33c..add255652 100644 --- a/src/modules/maxperip.c +++ b/src/modules/maxperip.c @@ -458,7 +458,17 @@ int maxperip_remote_connect(Client *client) const char *maxperip_allow_client(Client *client, ConfigItem_allow *aconf) { if (exceeds_maxperip(client, aconf)) + { + if (IsIPV6(client) && iConf.default_ipv6_clone_mask < 128) + { + char masked[16]; + mask_ipv6_rawip(client->rawip, iConf.default_ipv6_clone_mask, masked); + return format_ipv6_prefix_reject_message( + iConf.reject_message_too_many_connections_ipv6_range, + masked, iConf.default_ipv6_clone_mask); + } return iConf.reject_message_too_many_connections; + } return NULL; }