@@ -80,6 +80,7 @@ static void _query_set_dnssec(queriesData *query, const enum dnssec_status dnsse
8080static char * get_ptrname (const struct in_addr * addr );
8181static const char * check_dnsmasq_name (const char * name );
8282static void get_rcode (const unsigned short rcode , const char * * rcodestr , enum reply_type * reply );
83+ static bool client_matches_rate_limit_exempt_ip (const char * clientIP );
8384
8485// Static blocking metadata
8586static bool aabit = false, adbit = false, rabit = false;
@@ -106,6 +107,55 @@ static union mysockaddr last_server = {};
106107
107108const char * flagnames [] = {"F_IMMORTAL " , "F_NAMEP " , "F_REVERSE " , "F_FORWARD " , "F_DHCP " , "F_NEG " , "F_HOSTS " , "F_IPV4 " , "F_IPV6 " , "F_BIGNAME " , "F_NXDOMAIN " , "F_CNAME " , "F_DNSKEY " , "F_CONFIG " , "F_DS " , "F_DNSSECOK " , "F_UPSTREAM " , "F_RRNAME " , "F_SERVER " , "F_QUERY " , "F_NOERR " , "F_AUTH " , "F_DNSSEC " , "F_KEYTAG " , "F_SECSTAT " , "F_NO_RR " , "F_IPSET " , "F_NOEXTRA " , "F_DOMAINSRV" , "F_RCODE" , "F_RR" , "F_STALE" };
108109
110+ static bool client_matches_rate_limit_exempt_ip (const char * clientIP )
111+ {
112+ if (clientIP == NULL || config .dns .rateLimit .exemptIPs .v .json == NULL )
113+ return false;
114+
115+ struct in_addr client4 ;
116+ struct in6_addr client6 ;
117+ const sa_family_t family = inet_pton (AF_INET , clientIP , & client4 ) == 1 ? AF_INET :
118+ inet_pton (AF_INET6 , clientIP , & client6 ) == 1 ? AF_INET6 : AF_UNSPEC ;
119+ if (family == AF_UNSPEC )
120+ return false;
121+
122+ cJSON * item = NULL ;
123+ cJSON_ArrayForEach (item , config .dns .rateLimit .exemptIPs .v .json )
124+ {
125+ if (!cJSON_IsString (item ) || item -> valuestring == NULL )
126+ continue ;
127+
128+ const char * raw = item -> valuestring ;
129+ while (isspace ((unsigned char )* raw ))
130+ raw ++ ;
131+
132+ size_t len = strlen (raw );
133+ while (len > 0 && isspace ((unsigned char )raw [len - 1 ]))
134+ len -- ;
135+
136+ if (len == 0 || len > ADDRSTRLEN )
137+ continue ;
138+
139+ char exemptIP [ADDRSTRLEN + 1 ] = { 0 };
140+ memcpy (exemptIP , raw , len );
141+
142+ if (family == AF_INET )
143+ {
144+ struct in_addr exempt4 ;
145+ if (inet_pton (AF_INET , exemptIP , & exempt4 ) == 1 && memcmp (& client4 , & exempt4 , sizeof (exempt4 )) == 0 )
146+ return true;
147+ }
148+ else
149+ {
150+ struct in6_addr exempt6 ;
151+ if (inet_pton (AF_INET6 , exemptIP , & exempt6 ) == 1 && memcmp (& client6 , & exempt6 , sizeof (exempt6 )) == 0 )
152+ return true;
153+ }
154+ }
155+
156+ return false;
157+ }
158+
109159void FTL_hook (unsigned int flags , const char * name , const union all_addr * addr , char * arg , int id , unsigned short type , const char * file , const int line )
110160{
111161 // Extract filename from path
@@ -794,9 +844,10 @@ bool _FTL_new_query(const unsigned int flags, const char *name,
794844 // Interface name is only available for regular queries, not for
795845 // automatically generated DNSSEC queries
796846 const char * interface = internal_query ? "-" : next_iface .name ;
847+ const bool rate_limit_exempt = client_matches_rate_limit_exempt_ip (clientIP );
797848
798849 // Check rate-limit for this client
799- if (!internal_query && config .dns .rateLimit .count .v .ui > 0 &&
850+ if (!internal_query && ! rate_limit_exempt && config .dns .rateLimit .count .v .ui > 0 &&
800851 (++ client -> rate_limit > config .dns .rateLimit .count .v .ui || client -> flags .rate_limited ))
801852 {
802853 if (!client -> flags .rate_limited )
0 commit comments