E-MailRelay
gresolverfuture.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gresolverfuture.cpp
19///
20
21#include "gdef.h"
22#include "gresolverfuture.h"
23#include "ggetaddrinfo.h"
24#include "gtest.h"
25#include "gstr.h"
26#include "gidn.h"
27#include "gsleep.h"
28#include "glog.h"
29#include <cstring>
30#include <cstdio>
31
32GNet::ResolverFuture::ResolverFuture( const std::string & host , const std::string & service ,
33 int family , const Resolver::Config & config ) :
34 m_config(config) ,
35 m_numeric_service(!service.empty() && G::Str::isNumeric(service)) ,
36 m_host(encode(host,config.raw)) ,
37 m_host_p(m_host.c_str()) ,
38 m_service(service) ,
39 m_service_p(m_service.c_str()) ,
40 m_family(family)
41{
42 std::memset( &m_ai_hint , 0 , sizeof(m_ai_hint) ) ;
43 m_ai_hint.ai_family = family ;
44 m_ai_hint.ai_socktype = config.datagram ? SOCK_DGRAM : SOCK_STREAM ;
45 m_ai_hint.ai_flags = 0 ;
46 if( config.with_canonical_name ) m_ai_hint.ai_flags |= AI_CANONNAME ;
47 if( family == AF_UNSPEC ) m_ai_hint.ai_flags |= AI_ADDRCONFIG ;
48 if( m_numeric_service ) m_ai_hint.ai_flags |= AI_NUMERICSERV ;
49 #if GCONFIG_HAVE_GAI_IDN
50 if( config.idn_flag ) m_ai_hint.ai_flags |= AI_IDN ; // glibc only
51 #endif
52}
53
55{
56 if( m_ai )
57 GetAddrInfo::freeaddrinfo( m_ai ) ;
58}
59
60std::string GNet::ResolverFuture::encode( const std::string & host , bool raw )
61{
62 if( raw || G::Str::isPrintableAscii(host) )
63 return host ;
64 else if( G::Idn::valid(host) )
65 return G::Idn::encode( host ) ;
66 else
67 return host ;
68}
69
71{
72 // worker thread -- as simple as possible
73 if( m_config.test_slow ) sleep( 10 ) ;
74 m_rc = GetAddrInfo::getaddrinfo( m_host_p , m_service_p , &m_ai_hint , &m_ai ) ;
75 return *this ;
76}
77
78std::string GNet::ResolverFuture::failure() const
79{
80 std::stringstream ss ;
81 if( m_numeric_service )
82 ss << "no such " << ipvx() << "host: \"" << m_host << "\"" ;
83 else
84 ss << "no such " << ipvx() << "host or service: \"" << m_host << ":" << m_service << "\"" ;
85 const char * reason = GetAddrInfo::gai_strerror( m_rc ) ;
86 if( reason && *reason )
87 ss << " (" << G::Str::lower(G::Str::trimmed(std::string(reason)," .")) << ")" ;
88 return ss.str() ;
89}
90
91std::string GNet::ResolverFuture::ipvx() const
92{
93 if( m_family == AF_UNSPEC ) return {} ;
94 if( m_family == AF_INET ) return "ipv4 " ;
95 return "ipv6 " ; // AF_INET6 possibly undefined
96}
97
98bool GNet::ResolverFuture::failed() const
99{
100 return m_rc != 0 || m_ai == nullptr || m_ai->ai_addr == nullptr || m_ai->ai_addrlen == 0 ;
101}
102
103std::string GNet::ResolverFuture::none() const
104{
105 return "no usable addresses returned for \"" + m_host + "\"" ;
106}
107
108bool GNet::ResolverFuture::fetch( Result & result ) const
109{
110 // fetch the first valid address
111 for( const struct addrinfo * p = m_ai ; p ; p = p->ai_next )
112 {
113 socklen_t addrlen = static_cast<socklen_t>(p->ai_addrlen) ;
114 if( Address::validData( p->ai_addr , addrlen ) )
115 {
116 result.address = Address( p->ai_addr , addrlen ) ;
117 if( m_config.with_canonical_name && p->ai_canonname )
118 result.canonicalName.assign( p->ai_canonname ) ;
119 return true ;
120 }
121 }
122 return false ;
123}
124
125bool GNet::ResolverFuture::fetch( List & list ) const
126{
127 // fetch all valid addresses
128 bool got_one = false ;
129 for( const struct addrinfo * p = m_ai ; p ; p = p->ai_next )
130 {
131 socklen_t addrlen = static_cast<socklen_t>(p->ai_addrlen) ;
132 if( Address::validData( p->ai_addr , addrlen ) )
133 {
134 list.emplace_back( p->ai_addr , addrlen ) ;
135 got_one = true ;
136 }
137 }
138 return got_one ;
139}
140
141void GNet::ResolverFuture::get( List & list )
142{
143 if( failed() )
144 m_reason = failure() ;
145 else if( !fetch(list) )
146 m_reason = none() ;
147}
148
150{
151 Result result { Address::defaultAddress() , {} } ;
152 if( failed() )
153 m_reason = failure() ;
154 else if( !fetch(result) )
155 m_reason = none() ;
156 return result ;
157}
158
160{
161 return !m_reason.empty() ;
162}
163
165{
166 return m_reason ;
167}
168
static bool validData(const sockaddr *, socklen_t len)
Returns true if the sockaddr data is valid.
Definition: gaddress.cpp:458
static Address defaultAddress()
Returns a default address, being the IPv4 wildcard address with a zero port number.
Definition: gaddress.cpp:203
A 'future' shared-state class for asynchronous name resolution that holds parameters and results of a...
Result get()
Returns the resolved address after run() has completed.
bool error() const
Returns true if name resolution failed or no suitable address was returned.
std::string reason() const
Returns the reason for the error().
ResolverFuture(const std::string &host, const std::string &service, int family, const Resolver::Config &)
Constructor for resolving the given host and service names.
ResolverFuture & run() noexcept
Does the synchronous name resolution and stores the result.
~ResolverFuture()
Destructor.
static std::string lower(std::string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
Definition: gstr.cpp:824
static bool isPrintableAscii(std::string_view s) noexcept
Returns true if every character is between 0x20 and 0x7e inclusive.
Definition: gstr.cpp:413
static std::string trimmed(const std::string &s, std::string_view ws)
Returns a trim()med version of s.
Definition: gstr.cpp:343
std::string encode(std::string_view domain)
Returns the given domain with A-lables.
Definition: gidn.cpp:89
bool valid(std::string_view domain)
Returns true if the given domain is valid with U-labels and/or A-labels.
Definition: gidn.cpp:74
Low-level classes.
Definition: garg.h:36
Result structure for GNet::ResolverFuture::get().
A configuration structure for GNet::Resolver.
Definition: gresolver.h:52