]>
Commit | Line | Data |
---|---|---|
bbc27441 | 1 | /* |
f70aedc4 | 2 | * Copyright (C) 1996-2021 The Squid Software Foundation and contributors |
bbc27441 AJ |
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 | ||
714e68b7 AJ |
9 | #ifndef _SQUID_IPCACHE_H |
10 | #define _SQUID_IPCACHE_H | |
11 | ||
fd9c47d1 | 12 | #include "base/CbcPointer.h" |
4a3b98d7 | 13 | #include "dns/forward.h" |
fd9c47d1 AR |
14 | #include "ip/Address.h" |
15 | #include <iosfwd> | |
85e82959 | 16 | #include <vector> |
4a3b98d7 | 17 | |
fd9c47d1 AR |
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 | |
dc49061a | 24 | { |
3c670b50 | 25 | public: |
fd9c47d1 AR |
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; } | |
714e68b7 | 36 | |
fd9c47d1 AR |
37 | Ip::Address ip; |
38 | ||
39 | private: | |
40 | bool bad_ = false; ///< whether the address is currently deemed problematic | |
3c670b50 | 41 | }; |
714e68b7 | 42 | |
fd9c47d1 AR |
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 ¤t() 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 ¤t().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 ¤t() 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 | virtual ~IpReceiver() {} | |
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 | ||
4a3b98d7 | 219 | typedef void IPH(const ipcache_addrs *, const Dns::LookupDetails &details, void *); |
714e68b7 | 220 | |
8a648e8d FC |
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); | |
8a648e8d FC |
227 | void ipcacheMarkBadAddr(const char *name, const Ip::Address &); |
228 | void ipcacheMarkGoodAddr(const char *name, const Ip::Address &); | |
8a648e8d | 229 | void ipcacheFreeMemory(void); |
8a648e8d FC |
230 | void ipcache_restart(void); |
231 | int ipcacheAddEntryFromHosts(const char *name, const char *ipaddr); | |
714e68b7 | 232 | |
fd9c47d1 AR |
233 | inline std::ostream & |
234 | operator <<(std::ostream &os, const Dns::CachedIps &ips) | |
235 | { | |
236 | ips.reportCurrent(os); | |
237 | return os; | |
238 | } | |
239 | ||
240 | /* inlined implementations */ | |
241 | ||
242 | inline Dns::IpsSelector<Dns::GoodIpsIterator> | |
243 | Dns::CachedIps::good() const | |
244 | { | |
245 | return IpsSelector<GoodIpsIterator>(*this); | |
246 | } | |
247 | ||
248 | inline Dns::IpsSelector<Dns::IpsIterator> | |
249 | Dns::CachedIps::goodAndBad() const | |
250 | { | |
251 | return IpsSelector<IpsIterator>(*this); | |
252 | } | |
f53969cc | 253 | |
fd9c47d1 | 254 | #endif /* _SQUID_IPCACHE_H */ |
fbe83113 | 255 |