[ci] Fix clang-santisers job for GHA change
[xapian.git] / xapian-core / net / resolver.h
blob2f97007793ee0ddeead3ec00b818d8aedf5a0c7f
1 /** @file
2 * @brief Resolve hostnames and ip addresses
3 */
4 /* Copyright (C) 2017,2018,2024 Olly Betts
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #ifndef XAPIAN_INCLUDED_RESOLVER_H
22 #define XAPIAN_INCLUDED_RESOLVER_H
24 #include <cerrno>
25 #include <cstring>
26 #include <string_view>
28 #include "safenetdb.h"
29 #include "safesyssocket.h"
30 #include "str.h"
31 #include "xapian/error.h"
33 class Resolver {
34 struct addrinfo* result = NULL;
36 int eai_to_xapian(int e) {
37 // Under WIN32, the EAI_* constants are defined to be WSA_* constants
38 // with roughly equivalent meanings, so we can just let them be handled
39 // as any other WSA_* error codes would be.
40 #ifndef __WIN32__
41 // Ensure they all have the same sign - this switch will fail to
42 // compile if we bitwise-or some 1 and some 2 bits to get 3.
43 #define C(X) ((X) < 0 ? 2 : 1)
44 // Switch on a value there is a case for, to avoid clang warning: "no
45 // case matching constant switch condition '0'"
46 switch (3) {
47 case
48 C(EAI_AGAIN)|
49 C(EAI_BADFLAGS)|
50 C(EAI_FAIL)|
51 C(EAI_FAMILY)|
52 C(EAI_MEMORY)|
53 C(EAI_NONAME)|
54 C(EAI_SERVICE)|
55 C(EAI_SOCKTYPE)|
56 C(EAI_SYSTEM)|
57 #ifdef EAI_ADDRFAMILY
58 // In RFC 2553 but not RFC 3493 or POSIX:
59 C(EAI_ADDRFAMILY)|
60 #endif
61 #ifdef EAI_NODATA
62 // In RFC 2553 but not RFC 3493 or POSIX:
63 C(EAI_NODATA)|
64 #endif
65 #ifdef EAI_OVERFLOW
66 // In RFC 3493 and POSIX but not RFC 2553:
67 C(EAI_OVERFLOW)|
68 #endif
69 0: break;
70 case 3: break;
72 #undef C
74 // EAI_SYSTEM means "look at errno".
75 if (e == EAI_SYSTEM)
76 return errno;
77 // POSIX only says that EAI_* constants are "non-zero". On Linux they
78 // are negative, but allow for them being positive too.
79 if (EAI_FAIL > 0)
80 return -e;
81 #endif
82 return e;
85 public:
86 class const_iterator {
87 struct addrinfo* p;
88 public:
89 explicit const_iterator(struct addrinfo* p_) : p(p_) { }
91 struct addrinfo& operator*() const {
92 return *p;
95 void operator++() {
96 p = p->ai_next;
99 const_iterator operator++(int) {
100 struct addrinfo* old_p = p;
101 operator++();
102 return const_iterator(old_p);
105 bool operator==(const const_iterator& o) const {
106 return p == o.p;
109 bool operator!=(const const_iterator& o) const {
110 return !(*this == o);
114 Resolver(std::string_view host, int port, int flags = 0) {
115 using namespace std::string_literals;
116 // RFC 3493 has an extra sentence in its definition of
117 // AI_ADDRCONFIG which POSIX doesn't:
119 // "The loopback address is not considered for this case as valid
120 // as a configured address."
122 // Some platforms implement this version rather than POSIX (notably
123 // glibc on Linux). Others implement POSIX (from looking at the
124 // man pages, these include FreeBSD 11.0).
126 // In most cases, this extra sentence is arguably helpful - e.g. it
127 // means that you won't get IPv6 addresses just because the system
128 // has IPv6 loopback (and similarly for IPv4).
130 // However, it behaves unhelpfully if the *only* interface
131 // configured is loopback - in this situation, AI_ADDRCONFIG means
132 // that you won't get an IPv4 address (as there's no IPv4 address
133 // configured ignoring loopback) and you won't get an IPv6 address
134 // (as there's no IPv6 address configured ignoring loopback).
136 // It's generally rare that systems with only loopback would want
137 // to use the remote backend, but a real example is testsuites
138 // (including our own) running on autobuilders which deliberately
139 // close off network access.
141 // To allow such cases to work on Linux (and other platforms which
142 // follow the RFC rather than POSIX in this detail) we avoid using
143 // AI_ADDRCONFIG for 127.0.0.1, ::1 and localhost. There are
144 // other ways to write these IP addresses and other hostnames may
145 // map onto them, but this just needs to work for the standard
146 // cases which a testsuite might use.
147 if (host != "::1" && host != "127.0.0.1" && host != "localhost") {
148 flags |= AI_ADDRCONFIG;
150 // Not defined on older macOS (such as 10.5 which Apple no longer
151 // support but newer versions no longer support PowerPC Macs).
152 #ifdef AI_NUMERICSERV
153 flags |= AI_NUMERICSERV;
154 #endif
156 struct addrinfo hints;
157 std::memset(&hints, 0, sizeof(struct addrinfo));
158 hints.ai_family = AF_UNSPEC;
159 hints.ai_socktype = SOCK_STREAM;
160 hints.ai_flags = flags;
161 hints.ai_protocol = 0;
163 int r = getaddrinfo(host.empty() ? nullptr : str(host).c_str(),
164 str(port).c_str(), &hints, &result);
165 if (r != 0) {
166 throw Xapian::NetworkError("Couldn't resolve host "s.append(host),
167 eai_to_xapian(r));
171 ~Resolver() {
172 if (result) freeaddrinfo(result);
175 const_iterator begin() const {
176 return const_iterator(result);
179 const_iterator end() const {
180 return const_iterator(NULL);
184 #endif // XAPIAN_INCLUDED_RESOLVER_H