/src/libtorrent/src/resolver.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | |
3 | | Copyright (c) 2014-2017, 2019-2020, Arvid Norberg |
4 | | Copyright (c) 2016-2018, 2020, Alden Torres |
5 | | All rights reserved. |
6 | | |
7 | | Redistribution and use in source and binary forms, with or without |
8 | | modification, are permitted provided that the following conditions |
9 | | are met: |
10 | | |
11 | | * Redistributions of source code must retain the above copyright |
12 | | notice, this list of conditions and the following disclaimer. |
13 | | * Redistributions in binary form must reproduce the above copyright |
14 | | notice, this list of conditions and the following disclaimer in |
15 | | the documentation and/or other materials provided with the distribution. |
16 | | * Neither the name of the author nor the names of its |
17 | | contributors may be used to endorse or promote products derived |
18 | | from this software without specific prior written permission. |
19 | | |
20 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
21 | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
24 | | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
25 | | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | | POSSIBILITY OF SUCH DAMAGE. |
31 | | |
32 | | */ |
33 | | |
34 | | #include "libtorrent/aux_/resolver.hpp" |
35 | | #include "libtorrent/debug.hpp" |
36 | | #include "libtorrent/aux_/time.hpp" |
37 | | |
38 | | namespace libtorrent { |
39 | | namespace aux { |
40 | | |
41 | | |
42 | | constexpr resolver_flags resolver_interface::cache_only; |
43 | | constexpr resolver_flags resolver_interface::abort_on_shutdown; |
44 | | |
45 | | resolver::resolver(io_context& ios) |
46 | 1.69k | : m_ios(ios) |
47 | 1.69k | , m_resolver(ios) |
48 | 1.69k | , m_critical_resolver(ios) |
49 | 1.69k | , m_max_size(700) |
50 | 1.69k | , m_timeout(seconds(1200)) |
51 | 1.69k | {} |
52 | | |
53 | | namespace { |
54 | | void callback(resolver_interface::callback_t h |
55 | | , error_code const& ec, std::vector<address> const& ips) |
56 | 0 | { |
57 | 0 | try { |
58 | 0 | h(ec, ips); |
59 | 0 | } catch (std::exception&) { |
60 | 0 | TORRENT_ASSERT_FAIL(); |
61 | 0 | } |
62 | 0 | } |
63 | | } |
64 | | |
65 | | void resolver::on_lookup(error_code const& ec, tcp::resolver::results_type ips |
66 | | , std::string const& hostname) |
67 | 0 | { |
68 | 0 | COMPLETE_ASYNC("resolver::on_lookup"); |
69 | 0 | if (ec) |
70 | 0 | { |
71 | 0 | failed_dns_cache_entry& ce = m_failed_cache[hostname]; |
72 | 0 | ce.last_seen = time_now(); |
73 | 0 | ce.error = ec; |
74 | | |
75 | | // if the cache grows too big, weed out the |
76 | | // oldest entries |
77 | 0 | if (int(m_failed_cache.size()) > m_max_size) |
78 | 0 | { |
79 | 0 | auto oldest = m_failed_cache.begin(); |
80 | 0 | for (auto k = m_failed_cache.begin(); k != m_failed_cache.end(); ++k) |
81 | 0 | { |
82 | 0 | if (k->second.last_seen < oldest->second.last_seen) |
83 | 0 | oldest = k; |
84 | 0 | } |
85 | | |
86 | | // remove the oldest entry |
87 | 0 | m_failed_cache.erase(oldest); |
88 | 0 | } |
89 | |
|
90 | 0 | auto const range = m_callbacks.equal_range(hostname); |
91 | 0 | for (auto c = range.first; c != range.second; ++c) |
92 | 0 | callback(std::move(c->second), ec, {}); |
93 | 0 | m_callbacks.erase(range.first, range.second); |
94 | 0 | return; |
95 | 0 | } |
96 | | |
97 | 0 | { |
98 | 0 | auto const k = m_failed_cache.find(hostname); |
99 | 0 | if (k != m_failed_cache.end()) |
100 | 0 | m_failed_cache.erase(k); |
101 | 0 | } |
102 | |
|
103 | 0 | dns_cache_entry& ce = m_cache[hostname]; |
104 | 0 | ce.last_seen = time_now(); |
105 | 0 | ce.addresses.clear(); |
106 | 0 | for (auto i : ips) |
107 | 0 | ce.addresses.push_back(i.endpoint().address()); |
108 | |
|
109 | 0 | auto const range = m_callbacks.equal_range(hostname); |
110 | 0 | for (auto c = range.first; c != range.second; ++c) |
111 | 0 | callback(std::move(c->second), ec, ce.addresses); |
112 | 0 | m_callbacks.erase(range.first, range.second); |
113 | | |
114 | | // if m_cache grows too big, weed out the |
115 | | // oldest entries |
116 | 0 | if (int(m_cache.size()) > m_max_size) |
117 | 0 | { |
118 | 0 | auto oldest = m_cache.begin(); |
119 | 0 | for (auto k = m_cache.begin(); k != m_cache.end(); ++k) |
120 | 0 | { |
121 | 0 | if (k->second.last_seen < oldest->second.last_seen) |
122 | 0 | oldest = k; |
123 | 0 | } |
124 | | |
125 | | // remove the oldest entry |
126 | 0 | m_cache.erase(oldest); |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | void resolver::async_resolve(std::string const& host, resolver_flags const flags |
131 | | , resolver_interface::callback_t h) |
132 | 0 | { |
133 | | // special handling for raw IP addresses. There's no need to get in line |
134 | | // behind actual lookups if we can just resolve it immediately. |
135 | 0 | error_code ec; |
136 | 0 | address const ip = make_address(host, ec); |
137 | 0 | if (!ec) |
138 | 0 | { |
139 | 0 | post(m_ios, [h, ec, ip]{ callback(h, ec, std::vector<address>{ip}); }); |
140 | 0 | return; |
141 | 0 | } |
142 | 0 | ec.clear(); |
143 | |
|
144 | 0 | auto const i = m_cache.find(host); |
145 | 0 | if (i != m_cache.end()) |
146 | 0 | { |
147 | | // keep cache entries valid for m_timeout seconds |
148 | 0 | if ((flags & resolver_interface::cache_only) |
149 | 0 | || i->second.last_seen + m_timeout >= time_now()) |
150 | 0 | { |
151 | 0 | std::vector<address> ips = i->second.addresses; |
152 | 0 | post(m_ios, [h, ec, ips] { callback(h, ec, ips); }); |
153 | 0 | return; |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | 0 | auto const k = m_failed_cache.find(host); |
158 | 0 | if (k != m_failed_cache.end()) |
159 | 0 | { |
160 | | // keep cache entries valid for m_timeout seconds |
161 | | // failures are cached for a shorter time to optimistically retry |
162 | 0 | if ((flags & resolver_interface::cache_only) |
163 | 0 | || k->second.last_seen + m_timeout / 8 >= time_now()) |
164 | 0 | { |
165 | 0 | error_code error_code = k->second.error; |
166 | 0 | post(m_ios, [h, error_code] { callback(h, error_code, {}); }); |
167 | 0 | return; |
168 | 0 | } |
169 | 0 | } |
170 | | |
171 | 0 | if (flags & resolver_interface::cache_only) |
172 | 0 | { |
173 | | // we did not find a cache entry, fail the lookup |
174 | 0 | post(m_ios, [h] { |
175 | 0 | callback(h, boost::asio::error::host_not_found, std::vector<address>{}); |
176 | 0 | }); |
177 | 0 | return; |
178 | 0 | } |
179 | | |
180 | 0 | auto iter = m_callbacks.find(host); |
181 | 0 | bool const done = (iter != m_callbacks.end()); |
182 | |
|
183 | 0 | m_callbacks.insert(iter, {host, std::move(h)}); |
184 | | |
185 | | // if there is an existing outtanding lookup, our callback will be |
186 | | // called once it completes. We're done here. |
187 | 0 | if (done) return; |
188 | | |
189 | | // the port is ignored |
190 | 0 | using namespace std::placeholders; |
191 | 0 | ADD_OUTSTANDING_ASYNC("resolver::on_lookup"); |
192 | 0 | if (flags & resolver_interface::abort_on_shutdown) |
193 | 0 | { |
194 | 0 | m_resolver.async_resolve(host, "80", std::bind(&resolver::on_lookup, this, _1, _2 |
195 | 0 | , host)); |
196 | 0 | } |
197 | 0 | else |
198 | 0 | { |
199 | 0 | m_critical_resolver.async_resolve(host, "80", std::bind(&resolver::on_lookup, this, _1, _2 |
200 | 0 | , host)); |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | | void resolver::abort() |
205 | 1.69k | { |
206 | 1.69k | m_resolver.cancel(); |
207 | 1.69k | } |
208 | | |
209 | | void resolver::set_cache_timeout(seconds const timeout) |
210 | 1.69k | { |
211 | 1.69k | if (timeout >= seconds(0)) |
212 | 1.69k | m_timeout = timeout; |
213 | 0 | else |
214 | 0 | m_timeout = seconds(0); |
215 | 1.69k | } |
216 | | } |
217 | | } |