E-MailRelay
gresolverfuture.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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 "gtest.h"
24#include "gstr.h"
25#include "gsleep.h"
26#include "glog.h"
27#include <cstring>
28#include <cstdio>
29
30GNet::ResolverFuture::ResolverFuture( const std::string & host , const std::string & service , int family ,
31 bool dgram , bool for_async_hint ) :
32 m_numeric_service(!service.empty() && G::Str::isNumeric(service)) ,
33 m_socktype(dgram?SOCK_DGRAM:SOCK_STREAM) ,
34 m_host(host) ,
35 m_host_p(m_host.c_str()) ,
36 m_service(service) ,
37 m_service_p(m_service.c_str()) ,
38 m_family(family) ,
39 m_test_mode(for_async_hint&&G::Test::enabled("getaddrinfo-slow")) ,
40 m_rc(0) ,
41 m_ai(nullptr)
42{
43 std::memset( &m_ai_hint , 0 , sizeof(m_ai_hint) ) ;
44 m_ai_hint.ai_flags = AI_CANONNAME | // NOLINT
45 ( family == AF_UNSPEC ? AI_ADDRCONFIG : 0 ) |
46 ( m_numeric_service ? AI_NUMERICSERV : 0 ) ;
47 m_ai_hint.ai_family = family ;
48 m_ai_hint.ai_socktype = m_socktype ;
49}
50
52{
53 if( m_ai )
54 ::freeaddrinfo( m_ai ) ; // documented as "thread-safe"
55}
56
58{
59 // worker thread - as simple as possible
60 if( m_test_mode ) sleep( 10 ) ;
61 m_rc = ::getaddrinfo( m_host_p , m_service_p , &m_ai_hint , &m_ai ) ;
62 return *this ;
63}
64
65std::string GNet::ResolverFuture::failure() const
66{
67 std::stringstream ss ;
68 if( m_numeric_service )
69 ss << "no such " << ipvx() << "host: \"" << m_host << "\"" ;
70 else
71 ss << "no such " << ipvx() << "host or service: \"" << m_host << ":" << m_service << "\"" ;
72 const char * reason = gai_strerror( m_rc ) ; // not portable, but see gdef.h
73 if( reason && *reason )
74 ss << " (" << G::Str::lower(G::Str::trimmed(std::string(reason)," .")) << ")" ;
75 return ss.str() ;
76}
77
78std::string GNet::ResolverFuture::ipvx() const
79{
80 if( m_family == AF_UNSPEC ) return std::string() ;
81 if( m_family == AF_INET ) return "ipv4 " ;
82 return "ipv6 " ; // AF_INET6 possibly undefined
83}
84
85bool GNet::ResolverFuture::failed() const
86{
87 return m_rc != 0 || m_ai == nullptr || m_ai->ai_addr == nullptr || m_ai->ai_addrlen == 0 ;
88}
89
90std::string GNet::ResolverFuture::none() const
91{
92 return "no usable addresses returned for \"" + m_host + "\"" ;
93}
94
95bool GNet::ResolverFuture::fetch( Pair & pair ) const
96{
97 // fetch the first valid address/name pair
98 for( const struct addrinfo * p = m_ai ; p ; p = p->ai_next )
99 {
100 socklen_t addrlen = static_cast<socklen_t>(p->ai_addrlen) ;
101 if( Address::validData( p->ai_addr , addrlen ) )
102 {
103 Address address( p->ai_addr , addrlen ) ;
104 std::string name( p->ai_canonname ? p->ai_canonname : "" ) ;
105 pair = std::make_pair( address , name ) ;
106 return true ;
107 }
108 }
109 return false ;
110}
111
112bool GNet::ResolverFuture::fetch( List & list ) const
113{
114 // fetch all valid addresses
115 bool got_one = false ;
116 for( const struct addrinfo * p = m_ai ; p ; p = p->ai_next )
117 {
118 socklen_t addrlen = static_cast<socklen_t>(p->ai_addrlen) ;
119 if( Address::validData( p->ai_addr , addrlen ) )
120 {
121 list.push_back( Address( p->ai_addr , addrlen ) ) ;
122 got_one = true ;
123 }
124 }
125 return got_one ;
126}
127
128void GNet::ResolverFuture::get( List & list )
129{
130 if( failed() )
131 m_reason = failure() ;
132 else if( !fetch(list) )
133 m_reason = none() ;
134}
135
136GNet::ResolverFuture::Pair GNet::ResolverFuture::get()
137{
138 Pair result( Address::defaultAddress() , std::string() ) ;
139 if( failed() )
140 m_reason = failure() ;
141 else if( !fetch(result) )
142 m_reason = none() ;
143 return result ;
144}
145
147{
148 return !m_reason.empty() ;
149}
150
152{
153 return m_reason ;
154}
155
static bool validData(const sockaddr *, socklen_t len)
Returns true if the sockaddr data is valid.
Definition: gaddress.cpp:475
static Address defaultAddress()
Returns a default address, being the IPv4 wildcard address with a zero port number.
Definition: gaddress.cpp:242
A 'future' shared-state class for asynchronous name resolution that holds parameters and results of a...
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().
Pair get()
Returns the resolved address/name pair after run() has completed.
ResolverFuture(const std::string &host, const std::string &service, int family, bool dgram, bool for_async_hint=false)
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 trimmed(const std::string &s, string_view ws)
Returns a trim()med version of s.
Definition: gstr.cpp:364
static std::string lower(const std::string &s)
Returns a copy of 's' in which all Latin-1 upper-case characters have been replaced by lower-case cha...
Definition: gstr.cpp:741
Low-level classes.
Definition: galign.h:28