]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipcache.h
Maintenance: Consistent use of C++11 "override" specifier (#1224)
[thirdparty/squid.git] / src / ipcache.h
1 /*
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 #ifndef _SQUID_IPCACHE_H
10 #define _SQUID_IPCACHE_H
11
12 #include "base/CbcPointer.h"
13 #include "dns/forward.h"
14 #include "ip/Address.h"
15 #include <iosfwd>
16 #include <vector>
17
18 // The IPs the caller should not connect to are "bad". Other IPs are "good".
19
20 namespace Dns {
21
22 /// a CachedIps element
23 class CachedIp
24 {
25 public:
26 explicit CachedIp(const Ip::Address &anIp): ip(anIp) {}
27
28 /// whether the address is currently deemed problematic for any reason
29 bool bad() const { return bad_; }
30
31 /// mark the address as problematic; it might already be marked
32 void markAsBad() { bad_ = true; }
33
34 /// undo markAsBad()
35 void forgetMarking() { bad_ = false; }
36
37 Ip::Address ip;
38
39 private:
40 bool bad_ = false; ///< whether the address is currently deemed problematic
41 };
42
43 class IpsIterator;
44 class GoodIpsIterator;
45 template <class Iterator>
46 class IpsSelector;
47
48 /// A small container of IP addresses with a "current good address" getter API.
49 /// Ignores Ip::Address port.
50 class CachedIps
51 {
52 public:
53 /// whether we have at least one of the given IP addresses (ignoring ports)
54 /// upon success, also sets *position if the `position` is not nil
55 bool have(const Ip::Address &ip, size_t *position = nullptr) const;
56
57 /// \returns a good address
58 /// does not auto-rotate IPs but calling markAsBad() may change the answer
59 const Ip::Address &current() const { return ips.at(goodPosition).ip; }
60
61 bool empty() const noexcept { return ips.empty(); } ///< whether we cached no IPs at all
62 size_t size() const noexcept { return ips.size(); } ///< all cached IPs
63 size_t badCount() const noexcept { return badCount_; } ///< bad IPs
64
65 inline IpsSelector<GoodIpsIterator> good() const; ///< good IPs
66 inline IpsSelector<IpsIterator> goodAndBad() const; ///< all IPs
67
68 typedef std::vector<CachedIp> Storage;
69 const Storage &raw() const { return ips; } ///< all cached entries
70
71 /// Finds and marks the given address as bad, adjusting current() if needed.
72 /// Has no effect if the search fails or the found address is already bad.
73 /// XXX: An attempt to mark the last good address erases all marks instead.
74 /// XXX: It is impossible to successfully mark a single address as bad.
75 void markAsBad(const char *name, const Ip::Address &ip);
76
77 /// undo successful markAsBad()
78 void forgetMarking(const char *name, const Ip::Address &ip);
79
80 /// appends an IP address if we do not have() it already
81 /// invalidates all iterators
82 void pushUnique(const Ip::Address &ip);
83
84 /// replace all info with the given (presumed good) IP address
85 void reset(const Ip::Address &ip);
86
87 /// prints current IP and other debugging information
88 void reportCurrent(std::ostream &os) const;
89
90 private:
91 bool seekNewGood(const char *name);
92 void restoreGoodness(const char *name);
93
94 // Memory- and speed-optimized for "a few (and usually just one)" IPs,
95 // the vast majority of which are "good". The current implementation
96 // does linear searches and often reallocs when adding IPs.
97 Storage ips; ///< good and bad IPs
98
99 template <class Iterator> friend class IpsSelector;
100 size_t goodPosition = 0; ///< position of the IP returned by current()
101 size_t badCount_ = 0; ///< number of IPs that are currently marked as bad
102 };
103
104 // The CachedIps class keeps meta information about individual IP addresses
105 // together with those IPs. CachedIps users do not care about caching details;
106 // they just want to iterate (a subset of) cached IPs. The IpsIterator and
107 // IpsSelector classes below are minimal helper classes that make cached IPs
108 // iteration easier, safer, and copy-free. See also: CachedIps::good().
109
110 /// Iterates over any (good and/or bad) IPs in CachedIps, in unspecified order.
111 class IpsIterator
112 {
113 public:
114 typedef std::vector<CachedIp> Raw;
115 typedef Raw::const_iterator RawIterator;
116
117 // some of the standard iterator traits
118 using iterator_category = std::forward_iterator_tag;
119 using value_type = const Ip::Address;
120 using pointer = value_type *;
121 using reference = value_type &;
122
123 IpsIterator(const Raw &raw, const size_t): position_(raw.cbegin()) {}
124 // special constructor for end() iterator
125 explicit IpsIterator(const Raw &raw): position_(raw.cend()) {}
126
127 reference operator *() const { return position_->ip; }
128 pointer operator ->() const { return &position_->ip; }
129
130 IpsIterator& operator++() { ++position_; return *this; }
131 IpsIterator operator++(int) { const auto oldMe = *this; ++(*this); return oldMe; }
132
133 bool operator ==(const IpsIterator them) const { return position_ == them.position_; }
134 bool operator !=(const IpsIterator them) const { return !(*this == them); }
135
136 private:
137 RawIterator position_; ///< current iteration location
138 };
139
140 /// Iterates over good IPs in CachedIps, starting at the so called current one.
141 class GoodIpsIterator
142 {
143 public:
144 typedef std::vector<CachedIp> Raw;
145 typedef Raw::const_iterator RawIterator;
146
147 // some of the standard iterator traits
148 using iterator_category = std::forward_iterator_tag;
149 using value_type = const Ip::Address;
150 using pointer = value_type *;
151 using reference = value_type &;
152
153 GoodIpsIterator(const Raw &raw, const size_t currentPos): raw_(raw), position_(currentPos), processed_(0) { sync(); }
154 // special constructor for end() iterator
155 explicit GoodIpsIterator(const Raw &raw): raw_(raw), position_(0), processed_(raw.size()) {}
156
157 reference operator *() const { return current().ip; }
158 pointer operator ->() const { return &current().ip; }
159
160 GoodIpsIterator& operator++() { next(); sync(); return *this; }
161 GoodIpsIterator operator++(int) { const auto oldMe = *this; ++(*this); return oldMe; }
162
163 bool operator ==(const GoodIpsIterator them) const { return processed_ == them.processed_; }
164 bool operator !=(const GoodIpsIterator them) const { return !(*this == them); }
165
166 private:
167 const CachedIp &current() const { return raw_[position_ % raw_.size()]; }
168 void next() { ++position_; ++processed_; }
169 void sync() { while (processed_ < raw_.size() && current().bad()) next(); }
170
171 const Raw &raw_; ///< CachedIps being iterated
172 size_t position_; ///< current iteration location, modulo raw.size()
173 size_t processed_; ///< number of visited positions, including skipped ones
174 };
175
176 /// Makes "which IPs to iterate" decision explicit in range-based for loops.
177 /// Supported Iterator types are IpsIterator and GoodIpsIterator.
178 template <class Iterator>
179 class IpsSelector
180 {
181 public:
182 explicit IpsSelector(const CachedIps &ips): ips_(ips) {}
183
184 Iterator cbegin() const noexcept { return Iterator(ips_.raw(), ips_.goodPosition); }
185 Iterator cend() const noexcept { return Iterator(ips_.raw()); }
186 Iterator begin() const noexcept { return cbegin(); }
187 Iterator end() const noexcept { return cend(); }
188
189 private:
190 const CachedIps &ips_; ///< master IP storage we are wrapping
191 };
192
193 /// an interface for receiving IP::Addresses from nbgethostbyname()
194 class IpReceiver: public virtual CbdataParent
195 {
196 public:
197 ~IpReceiver() override {}
198
199 /// Called when nbgethostbyname() fully resolves the name.
200 /// The `ips` may contain both bad and good IP addresses, but each good IP
201 /// (if any) is guaranteed to had been previously reported via noteIp().
202 virtual void noteIps(const CachedIps *ips, const LookupDetails &details) = 0;
203
204 /// Called when/if nbgethostbyname() discovers a new good IP address.
205 virtual void noteIp(const Ip::Address &) {}
206
207 /// Called when/if nbgethostbyname() completes a single DNS lookup
208 /// if called, called before all the noteIp() calls for that DNS lookup.
209 virtual void noteLookup(const Dns::LookupDetails &) {}
210 };
211
212 /// initiate an (often) asynchronous DNS lookup; the `receiver` gets the results
213 void nbgethostbyname(const char *name, const CbcPointer<IpReceiver> &receiver);
214
215 } // namespace Dns
216
217 typedef Dns::CachedIps ipcache_addrs; ///< deprecated alias
218
219 typedef void IPH(const ipcache_addrs *, const Dns::LookupDetails &details, void *);
220
221 void ipcache_purgelru(void *);
222 void ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData);
223 const ipcache_addrs *ipcache_gethostbyname(const char *, int flags);
224 void ipcacheInvalidate(const char *);
225 void ipcacheInvalidateNegative(const char *);
226 void ipcache_init(void);
227 void ipcacheMarkBadAddr(const char *name, const Ip::Address &);
228 void ipcacheMarkGoodAddr(const char *name, const Ip::Address &);
229 void ipcache_restart(void);
230 int ipcacheAddEntryFromHosts(const char *name, const char *ipaddr);
231
232 inline std::ostream &
233 operator <<(std::ostream &os, const Dns::CachedIps &ips)
234 {
235 ips.reportCurrent(os);
236 return os;
237 }
238
239 /* inlined implementations */
240
241 inline Dns::IpsSelector<Dns::GoodIpsIterator>
242 Dns::CachedIps::good() const
243 {
244 return IpsSelector<GoodIpsIterator>(*this);
245 }
246
247 inline Dns::IpsSelector<Dns::IpsIterator>
248 Dns::CachedIps::goodAndBad() const
249 {
250 return IpsSelector<IpsIterator>(*this);
251 }
252
253 #endif /* _SQUID_IPCACHE_H */
254