61 HostList(
const std::vector<GNet::Address> & list ) : m_list(list) {}
62 const std::vector<GNet::Address> & m_list ;
64 std::ostream & operator<<( std::ostream & stream ,
const HostList & list )
66 const char * sep =
"" ;
67 for(
auto p = list.m_list.begin() ; p != list.m_list.end() ; ++p , sep =
" " )
69 stream << sep << (*p).hostPartString() ;
73 template <
typename T,
typename P>
77 for( ; p != end ; ++p )
80 result.push_back( (*p).server() ) ;
88 m_callback(callback) ,
90 m_timer(*this,&
DnsBlock::onTimeout,es) ,
92 m_allow_on_timeout(true) ,
93 m_dns_server(
Address::defaultAddress()) ,
105 configureImp( config ,
nullptr ) ;
107 catch( std::exception & e )
109 throw Error(
"invalid dnsbl configuration string" , e.what() ) ;
115 configureImp( config ,
this ) ;
118void GNet::DnsBlock::configureImp(
const std::string & config ,
DnsBlock * p )
121 if( list.size() < 4U )
122 throw std::runtime_error(
"not enough comma-sparated fields" ) ;
128 bool allow_on_timeout =
G::Str::toInt(list.at(1U)) >= 0 || threshold == 0U ;
129 unsigned int timeout_ms =
static_cast<unsigned int>( std::abs(
G::Str::toInt(list.at(1U))) ) ;
131 list.erase( list.begin() , list.begin()+3U ) ;
139 m_servers = servers ;
140 m_threshold = threshold ;
141 m_allow_on_timeout = allow_on_timeout ;
142 m_dns_server = dns_server ;
143 m_timeout = timeout ;
148 G_DEBUG(
"GNet::DnsBlock::start: dns-server=" << m_dns_server.displayString() <<
" "
149 <<
"threshold=" << m_threshold <<
" timeout=" << m_timeout <<
" "
151 <<
" servers=[" <<
G::Str::join(
",",m_servers) <<
"]" ) ;
153 m_result.reset( m_threshold , address ) ;
158 if( m_servers.empty() || is_local )
160 m_timer.startTimer( 0 ) ;
165 static unsigned int id_generator = 10 ;
166 if( (id_generator+m_servers.size()) > 65535U )
170 m_socket_ptr = std::make_unique<DatagramSocket>( m_dns_server.family() ) ;
171 m_socket_ptr->addReadHandler( *
this , m_es ) ;
174 std::string prefix = queryString( address ) ;
175 unsigned int id = m_id_base = id_generator ;
176 for( G::StringArray::const_iterator server_p = m_servers.begin() ; server_p != m_servers.end() ;
177 ++server_p ,
id++ , id_generator++ )
183 const char * type = address.
family() == Address::Family::ipv4 ?
"A" :
"AAAA" ;
185 G_DEBUG(
"GNet::DnsBlock::start: sending [" << prefix <<
"."
186 << server <<
"] to [" << m_dns_server.displayString() <<
"]: id " <<
id ) ;
188 ssize_t rc = m_socket_ptr->writeto( message.
p() , message.
n() , m_dns_server ) ;
189 if( rc < 0 ||
static_cast<std::size_t
>(rc) != message.
n() )
190 throw Error(
"socket send failed" , m_socket_ptr->reason() ) ;
192 m_timer.startTimer( m_timeout ) ;
197 return m_timer.active() ;
200void GNet::DnsBlock::readEvent()
202 static std::vector<char> buffer;
203 buffer.resize( 4096U ) ;
204 ssize_t rc = m_socket_ptr->read( &buffer[0] , buffer.size() ) ;
205 if( rc <= 0 ||
static_cast<std::size_t
>(rc) >= buffer.size() )
206 throw Error(
"invalid dns response size" ) ;
207 buffer.resize(
static_cast<std::size_t
>(rc) ) ;
210 if( !message.QR() || message.ID() < m_id_base ||
211 message.ID() >= (m_id_base+m_servers.size()) || message.RCODE() > 5 )
213 G_WARNING(
"GNet::DnsBlock::readEvent: invalid dns response: qr=" << message.QR()
214 <<
" rcode=" << message.RCODE() <<
" id=" << message.ID() ) ;
218 m_result.at(message.ID()-m_id_base).set( message.addresses() ) ;
220 std::size_t server_count = m_result.list().size() ;
221 std::size_t responder_count = countResponders( m_result.list() ) ;
222 std::size_t laggard_count = server_count - responder_count ; G_ASSERT( laggard_count < server_count ) ;
223 std::size_t deny_count = countDeniers( m_result.list() ) ;
225 G_DEBUG(
"GNet::DnsBlock::readEvent: id=" << message.ID() <<
" rcode=" << message.RCODE()
226 << (message.ANCOUNT()?
" deny ":
" allow ")
227 <<
"got=" << responder_count <<
"/" << server_count <<
" deny-count=" << deny_count <<
"/" << m_threshold ) ;
229 bool finished = m_timer.active() && (
230 responder_count == server_count ||
231 ( m_threshold && deny_count >= m_threshold ) ||
232 ( m_threshold && (deny_count+laggard_count) < m_threshold ) ) ;
236 m_socket_ptr.reset() ;
237 m_timer.cancelTimer() ;
238 m_result.type() = ( m_threshold && deny_count >= m_threshold ) ?
239 DnsBlockResult::Type::Deny :
240 DnsBlockResult::Type::Allow ;
241 m_callback.onDnsBlockResult( m_result ) ;
245void GNet::DnsBlock::onTimeout()
247 m_socket_ptr.reset() ;
248 m_result.type() = m_result.list().empty() ?
249 ( m_servers.empty() ? DnsBlockResult::Type::Inactive : DnsBlockResult::Type::Local ) :
250 ( m_allow_on_timeout ? DnsBlockResult::Type::TimeoutAllow : DnsBlockResult::Type::TimeoutDeny ) ;
251 m_callback.onDnsBlockResult( m_result ) ;
254std::string GNet::DnsBlock::queryString(
const Address & address )
256 return address.queryString() ;
261 using namespace DnsBlockImp ;
262 if( m_type == Type::Local )
264 G_LOG(
"GNet::DnsBlockResult::log: dnsbl: not checking local address [" << m_address.hostPartString() <<
"]" ) ;
266 else if( m_type != Type::Inactive )
268 for(
const auto & result : m_list )
270 std::ostringstream ss ;
271 ss <<
"address [" << m_address.hostPartString() <<
"] " ;
272 if( result.valid() && result.addresses().empty() )
273 ss <<
"allowed by [" << result.server() <<
"]" ;
274 else if( result.valid() )
275 ss <<
"denied by [" << result.server() <<
"]: " << HostList(result.addresses()) ;
277 ss <<
"not checked by [" << result.server() <<
"]" ;
278 G_LOG(
"GNet::DnsBlockResult::log: dnsbl: " << ss.str() ) ;
285 if( m_type == Type::Deny || m_type == Type::TimeoutDeny || m_type == Type::TimeoutAllow )
287 std::ostringstream ss ;
288 ss <<
"client address [" << m_address.hostPartString() <<
"]" ;
289 if( m_type == Type::Deny || m_type == Type::TimeoutDeny )
291 if( m_type == Type::TimeoutDeny || m_type == Type::TimeoutAllow )
292 ss <<
": timeout: no answer from [" <<
G::Str::join(
"] [",laggards()) <<
"]" ;
295 G_WARNING(
"GNet::DnsBlockResult::log: dnsbl: " << ss.str() ) ;
301 return m_type == Type::Inactive || m_type == Type::Local || m_type == Type::TimeoutAllow || m_type == Type::Allow ;
309std::size_t GNet::DnsBlock::countResponders(
const ResultList & list )
311 using namespace DnsBlockImp ;
312 return static_cast<std::size_t
>(std::count_if(list.begin(),list.end(),Responder())) ;
315std::size_t GNet::DnsBlock::countDeniers(
const ResultList & list )
317 using namespace DnsBlockImp ;
318 return static_cast<std::size_t
>(std::count_if(list.begin(),list.end(),Denier())) ;
323 using namespace DnsBlockImp ;
324 return server_names_if( m_list.begin() , m_list.end() , Denier() ) ;
329 using namespace DnsBlockImp ;
330 return server_names_if( m_list.begin() , m_list.end() , Laggard() ) ;
The GNet::Address class encapsulates a TCP/UDP transport address.
Family family() const
Returns the address family enumeration.
bool isLinkLocal() const
Returns true if this is a link-local address.
std::string hostPartString(bool raw=false) const
Returns a string which represents the network address.
bool isUniqueLocal() const
Returns true if this is a locally administered address.
static Address parse(const std::string &display_string)
Factory function for any address family.
bool isLoopback() const
Returns true if this is a loopback address.
A callback interface for GNet::DnsBlock.
void warn() const
Emits warnings.
bool allow() const
Returns true if the type is Inactive, Local, TimeoutAllow or Allow.
G::StringArray laggards() const
Returns the list of slow or unresponsive servers.
G::StringArray deniers() const
Returns the list of denying servers.
void log() const
Logs the results.
bool deny() const
Returns true if the type is TimeoutDeny or Deny.
A result structure for one DNSBL server.
bool valid() const
Returns true if the list() is valid.
const std::vector< Address > & addresses() const
Returns the result list, which is empty if there is no block or not valid().
Implements DNS blocklisting, as per RFC-5782.
static void checkConfig(const std::string &)
Checks the configure() string, throwing on error.
DnsBlock(DnsBlockCallback &, ExceptionSink, const std::string &config=std::string())
Constructor.
void configure(const Address &dns_server, std::size_t threshold, bool allow_on_timeout, G::TimeInterval timeout, const G::StringArray &servers)
Configures the object after construction.
void start(const Address &)
Starts an asychronous check on the given address.
bool busy() const
Returns true after start() and before the completion callback.
A DNS message parser, with static factory functions for message composition.
std::size_t n() const
Returns the raw data size.
const char * p() const
Returns the raw data.
static DnsMessage request(const std::string &type, const std::string &hostname, unsigned int id=0U)
Factory function for a request message of the give type ("A", "AAAA", etc).
A tuple containing an ExceptionHandler interface pointer and a bound 'exception source' pointer.
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings with separators.
static string_view ws()
Returns a string of standard whitespace characters.
static void splitIntoFields(const std::string &in, StringArray &out, string_view ws, char escape='\0', bool remove_escapes=true)
Splits the string into fields.
static int toInt(const std::string &s)
Converts string 's' to an int.
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
static std::string trimmed(const std::string &s, string_view ws)
Returns a trim()med version of s.
static bool enabled() noexcept
Returns true if test features are enabled.
An interval between two G::SystemTime values or two G::TimerTime values.
std::vector< std::string > StringArray
A std::vector of std::strings.
Overload discriminator for Address::parse()
A functor that tests a GNet::DnsBlockServerResult.
A list of addresses used by GNet::DnsBlock.
A functor that tests a GNet::DnsBlockServerResult.
A functor that tests a GNet::DnsBlockServerResult.