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
32 GNet::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
54 GNet::ResolverFuture::~ResolverFuture()
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
70 GNet::ResolverFuture & GNet::ResolverFuture::run() noexcept
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
98 bool 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
108 bool 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
125 bool 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
141 void GNet::ResolverFuture::get( List & list )
142{
143 if( failed() )
144 m_reason = failure() ;
145 else if( !fetch(list) )
146 m_reason = none() ;
147}
148
149 GNet::ResolverFuture::Result GNet::ResolverFuture::get()
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
159 bool GNet::ResolverFuture::error() const
160{
161 return !m_reason.empty() ;
162}
163
164std::string GNet::ResolverFuture::reason() const
165{
166 return m_reason ;
167}
168
GNet::Address::validData
static bool validData(const sockaddr *, socklen_t len)
Returns true if the sockaddr data is valid.
Definition: gaddress.cpp:458
GNet::Address::defaultAddress
static Address defaultAddress()
Returns a default address, being the IPv4 wildcard address with a zero port number.
Definition: gaddress.cpp:203
GNet::ResolverFuture
A 'future' shared-state class for asynchronous name resolution that holds parameters and results of a...
Definition: gresolverfuture.h:58
GNet::ResolverFuture::get
Result get()
Returns the resolved address after run() has completed.
Definition: gresolverfuture.cpp:149
GNet::ResolverFuture::error
bool error() const
Returns true if name resolution failed or no suitable address was returned.
Definition: gresolverfuture.cpp:159
GNet::ResolverFuture::reason
std::string reason() const
Returns the reason for the error().
Definition: gresolverfuture.cpp:164
GNet::ResolverFuture::ResolverFuture
ResolverFuture(const std::string &host, const std::string &service, int family, const Resolver::Config &)
Constructor for resolving the given host and service names.
Definition: gresolverfuture.cpp:32
GNet::ResolverFuture::run
ResolverFuture & run() noexcept
Does the synchronous name resolution and stores the result.
Definition: gresolverfuture.cpp:70
GNet::ResolverFuture::~ResolverFuture
~ResolverFuture()
Destructor.
Definition: gresolverfuture.cpp:54
G::Str::lower
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
G::Str::isPrintableAscii
static bool isPrintableAscii(std::string_view s) noexcept
Returns true if every character is between 0x20 and 0x7e inclusive.
Definition: gstr.cpp:413
G::Str::trimmed
static std::string trimmed(const std::string &s, std::string_view ws)
Returns a trim()med version of s.
Definition: gstr.cpp:343
ggetaddrinfo.h
gidn.h
glog.h
gresolverfuture.h
gsleep.h
gstr.h
gtest.h
G::Idn::encode
std::string encode(std::string_view domain)
Returns the given domain with A-lables.
Definition: gidn.cpp:89
G::Idn::valid
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
G
Low-level classes.
Definition: garg.h:36
GNet::ResolverFuture::Result
Result structure for GNet::ResolverFuture::get().
Definition: gresolverfuture.h:61
GNet::Resolver::Config
A configuration structure for GNet::Resolver.
Definition: gresolver.h:52