2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "dnsdist-protocols.hh"
29 #include "uuid-utils.hh"
34 class DNSDistPacketCache;
36 using QTag = std::unordered_map<string, string>;
40 StopWatch(bool realTime = false) :
41 d_needRealTime(realTime)
47 d_start = getCurrentTime();
50 void set(const struct timespec& from)
57 struct timespec now = getCurrentTime();
58 return 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0;
63 struct timespec now = getCurrentTime();
64 auto ret = 1000000.0 * (now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec) / 1000.0;
69 struct timespec getStartTime() const
74 struct timespec d_start
80 struct timespec getCurrentTime() const
83 if (gettime(&now, d_needRealTime) < 0) {
84 unixDie("Getting timestamp");
92 /* g++ defines __SANITIZE_THREAD__
93 clang++ supports the nice __has_feature(thread_sanitizer),
95 #if defined(__has_feature)
96 #if __has_feature(thread_sanitizer)
97 #define __SANITIZE_THREAD__ 1
101 struct InternalQueryState
103 static void DeleterPlaceHolder(DOHUnit*)
107 InternalQueryState() :
108 du(std::unique_ptr<DOHUnit, void (*)(DOHUnit*)>(nullptr, DeleterPlaceHolder))
110 origDest.sin4.sin_family = 0;
113 InternalQueryState(InternalQueryState&& rhs) = default;
114 InternalQueryState& operator=(InternalQueryState&& rhs) = default;
116 InternalQueryState(const InternalQueryState& orig) = delete;
117 InternalQueryState& operator=(const InternalQueryState& orig) = delete;
119 boost::optional<Netmask> subnet{boost::none}; // 40
120 ComboAddress origRemote; // 28
121 ComboAddress origDest; // 28
122 ComboAddress hopRemote;
123 ComboAddress hopLocal;
125 std::string poolName; // 24
126 StopWatch queryRealTime{true}; // 16
127 std::shared_ptr<DNSDistPacketCache> packetCache{nullptr}; // 16
128 std::unique_ptr<DNSCryptQuery> dnsCryptQuery{nullptr}; // 8
129 std::unique_ptr<QTag> qTag{nullptr}; // 8
130 boost::optional<uint32_t> tempFailureTTL{boost::none}; // 8
131 ClientState* cs{nullptr}; // 8
132 std::unique_ptr<DOHUnit, void (*)(DOHUnit*)> du; // 8
133 uint32_t cacheKey{0}; // 4
134 uint32_t cacheKeyNoECS{0}; // 4
136 uint32_t cacheKeyUDP{0}; // 4
137 uint32_t ttlCap{0}; // cap the TTL _after_ inserting into the packet cache // 4
138 int backendFD{-1}; // 4
140 uint16_t qtype{0}; // 2
141 uint16_t qclass{0}; // 2
142 // origID is in network-byte order
143 uint16_t origID{0}; // 2
144 uint16_t origFlags{0}; // 2
145 uint16_t cacheFlags{0}; // DNS flags as sent to the backend // 2
146 uint16_t udpPayloadSize{0}; // Max UDP payload size from the query // 2
147 dnsdist::Protocol protocol; // 1
148 boost::optional<boost::uuids::uuid> uniqueId{boost::none}; // 17 (placed here to reduce the space lost to padding)
149 bool ednsAdded{false};
150 bool ecsAdded{false};
151 bool skipCache{false};
152 bool dnssecOK{false};
153 bool useZeroScope{false};
154 bool forwardedOverUDP{false};
163 IDState(const IDState& orig) = delete;
164 IDState(IDState&& rhs) noexcept :
165 internal(std::move(rhs.internal))
167 inUse.store(rhs.inUse.load());
168 age.store(rhs.age.load());
171 IDState& operator=(IDState&& rhs) noexcept
173 inUse.store(rhs.inUse.load());
174 age.store(rhs.age.load());
175 internal = std::move(rhs.internal);
184 /* For performance reasons we don't want to use a lock here, but that means
185 we need to be very careful when modifying this value. Modifications happen
187 - one of the UDP or DoH 'client' threads receiving a query, selecting a backend
188 then picking one of the states associated to this backend (via the idOffset).
189 Most of the time this state should not be in use and usageIndicator is -1, but we
190 might not yet have received a response for the query previously associated to this
191 state, meaning that we will 'reuse' this state and erase the existing state.
192 If we ever receive a response for this state, it will be discarded. This is
193 mostly fine for UDP except that we still need to be careful in order to miss
194 the 'outstanding' counters, which should only be increased when we are picking
195 an empty state, and not when reusing ;
196 For DoH, though, we have dynamically allocated a DOHUnit object that needs to
197 be freed, as well as internal objects internals to libh2o.
198 - one of the UDP receiver threads receiving a response from a backend, picking
199 the corresponding state and sending the response to the client ;
200 - the 'healthcheck' thread scanning the states to actively discover timeouts,
201 mostly to keep some counters like the 'outstanding' one sane.
204 - inUse tells us if there currently is a in-flight query whose state is stored
206 - locked tells us whether someone currently owns the state, so no-one else can touch
209 InternalQueryState internal;
210 std::atomic<uint16_t> age{0};
215 StateGuard(IDState& ids) :
223 StateGuard(const StateGuard&) = delete;
224 StateGuard(StateGuard&&) = delete;
225 StateGuard& operator=(const StateGuard&) = delete;
226 StateGuard& operator=(StateGuard&&) = delete;
232 [[nodiscard]] std::optional<StateGuard> acquire()
234 bool expected = false;
235 if (locked.compare_exchange_strong(expected, true)) {
236 return std::optional<StateGuard>(*this);
246 std::atomic<bool> inUse{false}; // 1
249 std::atomic<bool> locked{false}; // 1