1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2016 Tatsuhiro Tsujikawa
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25 #include "shrpx_dns_resolver.h"
26
27 #include <cstring>
28 #include <sys/time.h>
29
30 #include "shrpx_log.h"
31 #include "shrpx_connection.h"
32 #include "shrpx_config.h"
33
34 namespace shrpx {
35
36 namespace {
sock_state_cb(void * data,int s,int read,int write)37 void sock_state_cb(void *data, int s, int read, int write) {
38 auto resolv = static_cast<DNSResolver *>(data);
39
40 if (resolv->get_status(nullptr) != DNSResolverStatus::RUNNING) {
41 return;
42 }
43
44 if (read) {
45 resolv->start_rev(s);
46 } else {
47 resolv->stop_rev(s);
48 }
49 if (write) {
50 resolv->start_wev(s);
51 } else {
52 resolv->stop_wev(s);
53 }
54 }
55 } // namespace
56
57 namespace {
addrinfo_cb(void * arg,int status,int timeouts,ares_addrinfo * result)58 void addrinfo_cb(void *arg, int status, int timeouts, ares_addrinfo *result) {
59 auto resolv = static_cast<DNSResolver *>(arg);
60 resolv->on_result(status, result);
61
62 ares_freeaddrinfo(result);
63 }
64 } // namespace
65
66 namespace {
process_result(DNSResolver * resolv)67 void process_result(DNSResolver *resolv) {
68 auto cb = resolv->get_complete_cb();
69 if (!cb) {
70 return;
71 }
72 Address result;
73 auto status = resolv->get_status(&result);
74 switch (status) {
75 case DNSResolverStatus::OK:
76 case DNSResolverStatus::ERROR:
77 cb(status, &result);
78 break;
79 default:
80 break;
81 }
82 // resolv may be deleted here.
83 }
84 } // namespace
85
86 namespace {
readcb(struct ev_loop * loop,ev_io * w,int revents)87 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
88 auto resolv = static_cast<DNSResolver *>(w->data);
89 resolv->on_read(w->fd);
90 process_result(resolv);
91 }
92 } // namespace
93
94 namespace {
writecb(struct ev_loop * loop,ev_io * w,int revents)95 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
96 auto resolv = static_cast<DNSResolver *>(w->data);
97 resolv->on_write(w->fd);
98 process_result(resolv);
99 }
100 } // namespace
101
102 namespace {
timeoutcb(struct ev_loop * loop,ev_timer * w,int revents)103 void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
104 auto resolv = static_cast<DNSResolver *>(w->data);
105 resolv->on_timeout();
106 process_result(resolv);
107 }
108 } // namespace
109
110 namespace {
stop_ev(struct ev_loop * loop,const std::vector<std::unique_ptr<ev_io>> & evs)111 void stop_ev(struct ev_loop *loop,
112 const std::vector<std::unique_ptr<ev_io>> &evs) {
113 for (auto &w : evs) {
114 ev_io_stop(loop, w.get());
115 }
116 }
117 } // namespace
118
DNSResolver(struct ev_loop * loop)119 DNSResolver::DNSResolver(struct ev_loop *loop)
120 : result_{},
121 loop_(loop),
122 channel_(nullptr),
123 family_(AF_UNSPEC),
124 status_(DNSResolverStatus::IDLE) {
125 ev_timer_init(&timer_, timeoutcb, 0., 0.);
126 timer_.data = this;
127 }
128
~DNSResolver()129 DNSResolver::~DNSResolver() {
130 if (channel_) {
131 ares_destroy(channel_);
132 }
133
134 stop_ev(loop_, revs_);
135 stop_ev(loop_, wevs_);
136
137 ev_timer_stop(loop_, &timer_);
138 }
139
resolve(const StringRef & name,int family)140 int DNSResolver::resolve(const StringRef &name, int family) {
141 if (status_ != DNSResolverStatus::IDLE) {
142 return -1;
143 }
144
145 if (LOG_ENABLED(INFO)) {
146 LOG(INFO) << "Start resolving host " << name << " in IPv"
147 << (family == AF_INET ? "4" : "6");
148 }
149
150 name_ = name;
151 family_ = family;
152
153 int rv;
154
155 auto &dnsconf = get_config()->dns;
156
157 ares_options opts{};
158 opts.sock_state_cb = sock_state_cb;
159 opts.sock_state_cb_data = this;
160 opts.timeout = static_cast<int>(dnsconf.timeout.lookup * 1000);
161 opts.tries = dnsconf.max_try;
162
163 auto optmask = ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
164
165 ares_channel chan;
166 rv = ares_init_options(&chan, &opts, optmask);
167 if (rv != ARES_SUCCESS) {
168 if (LOG_ENABLED(INFO)) {
169 LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv);
170 }
171 status_ = DNSResolverStatus::ERROR;
172 return -1;
173 }
174
175 channel_ = chan;
176 status_ = DNSResolverStatus::RUNNING;
177
178 ares_addrinfo_hints hints{};
179 hints.ai_family = family_;
180
181 ares_getaddrinfo(channel_, name_.data(), nullptr, &hints, addrinfo_cb, this);
182 reset_timeout();
183
184 return 0;
185 }
186
on_read(int fd)187 int DNSResolver::on_read(int fd) { return handle_event(fd, ARES_SOCKET_BAD); }
188
on_write(int fd)189 int DNSResolver::on_write(int fd) { return handle_event(ARES_SOCKET_BAD, fd); }
190
on_timeout()191 int DNSResolver::on_timeout() {
192 return handle_event(ARES_SOCKET_BAD, ARES_SOCKET_BAD);
193 }
194
handle_event(int rfd,int wfd)195 int DNSResolver::handle_event(int rfd, int wfd) {
196 if (status_ == DNSResolverStatus::IDLE) {
197 return -1;
198 }
199
200 ares_process_fd(channel_, rfd, wfd);
201
202 switch (status_) {
203 case DNSResolverStatus::RUNNING:
204 reset_timeout();
205 return 0;
206 case DNSResolverStatus::OK:
207 return 0;
208 case DNSResolverStatus::ERROR:
209 return -1;
210 default:
211 // Unreachable
212 assert(0);
213 abort();
214 }
215 }
216
reset_timeout()217 void DNSResolver::reset_timeout() {
218 if (status_ != DNSResolverStatus::RUNNING) {
219 return;
220 }
221 timeval tvout;
222 auto tv = ares_timeout(channel_, nullptr, &tvout);
223 if (tv == nullptr) {
224 return;
225 }
226 // To avoid that timer_.repeat becomes 0, which makes ev_timer_again
227 // useless, add tiny fraction of time.
228 timer_.repeat = tv->tv_sec + tv->tv_usec / 1000000. + 1e-9;
229 ev_timer_again(loop_, &timer_);
230 }
231
get_status(Address * result) const232 DNSResolverStatus DNSResolver::get_status(Address *result) const {
233 if (status_ != DNSResolverStatus::OK) {
234 return status_;
235 }
236
237 if (result) {
238 memcpy(result, &result_, sizeof(result_));
239 }
240
241 return status_;
242 }
243
244 namespace {
start_ev(std::vector<std::unique_ptr<ev_io>> & evs,struct ev_loop * loop,int fd,int event,IOCb cb,void * data)245 void start_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
246 int fd, int event, IOCb cb, void *data) {
247 for (auto &w : evs) {
248 if (w->fd == fd) {
249 return;
250 }
251 }
252 for (auto &w : evs) {
253 if (w->fd == -1) {
254 ev_io_set(w.get(), fd, event);
255 ev_io_start(loop, w.get());
256 return;
257 }
258 }
259
260 auto w = std::make_unique<ev_io>();
261 ev_io_init(w.get(), cb, fd, event);
262 w->data = data;
263 ev_io_start(loop, w.get());
264 evs.emplace_back(std::move(w));
265 }
266 } // namespace
267
268 namespace {
stop_ev(std::vector<std::unique_ptr<ev_io>> & evs,struct ev_loop * loop,int fd,int event)269 void stop_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
270 int fd, int event) {
271 for (auto &w : evs) {
272 if (w->fd == fd) {
273 ev_io_stop(loop, w.get());
274 ev_io_set(w.get(), -1, event);
275 return;
276 }
277 }
278 }
279 } // namespace
280
start_rev(int fd)281 void DNSResolver::start_rev(int fd) {
282 start_ev(revs_, loop_, fd, EV_READ, readcb, this);
283 }
284
stop_rev(int fd)285 void DNSResolver::stop_rev(int fd) { stop_ev(revs_, loop_, fd, EV_READ); }
286
start_wev(int fd)287 void DNSResolver::start_wev(int fd) {
288 start_ev(wevs_, loop_, fd, EV_WRITE, writecb, this);
289 }
290
stop_wev(int fd)291 void DNSResolver::stop_wev(int fd) { stop_ev(wevs_, loop_, fd, EV_WRITE); }
292
on_result(int status,ares_addrinfo * ai)293 void DNSResolver::on_result(int status, ares_addrinfo *ai) {
294 stop_ev(loop_, revs_);
295 stop_ev(loop_, wevs_);
296 ev_timer_stop(loop_, &timer_);
297
298 if (status != ARES_SUCCESS) {
299 if (LOG_ENABLED(INFO)) {
300 LOG(INFO) << "Name lookup for " << name_
301 << " failed: " << ares_strerror(status);
302 }
303 status_ = DNSResolverStatus::ERROR;
304 return;
305 }
306
307 auto ap = ai->nodes;
308
309 for (; ap; ap = ap->ai_next) {
310 switch (ap->ai_family) {
311 case AF_INET:
312 status_ = DNSResolverStatus::OK;
313 result_.len = sizeof(result_.su.in);
314
315 assert(sizeof(result_.su.in) == ap->ai_addrlen);
316
317 memcpy(&result_.su.in, ap->ai_addr, sizeof(result_.su.in));
318
319 break;
320 case AF_INET6:
321 status_ = DNSResolverStatus::OK;
322 result_.len = sizeof(result_.su.in6);
323
324 assert(sizeof(result_.su.in6) == ap->ai_addrlen);
325
326 memcpy(&result_.su.in6, ap->ai_addr, sizeof(result_.su.in6));
327
328 break;
329 default:
330 continue;
331 }
332
333 break;
334 }
335
336 if (!ap) {
337 if (LOG_ENABLED(INFO)) {
338 LOG(INFO) << "Name lookup for " << name_
339 << " failed: no address returned";
340 }
341 status_ = DNSResolverStatus::ERROR;
342 return;
343 }
344
345 if (status_ == DNSResolverStatus::OK) {
346 if (LOG_ENABLED(INFO)) {
347 LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
348 << util::numeric_name(&result_.su.sa, result_.len);
349 }
350 return;
351 }
352
353 status_ = DNSResolverStatus::ERROR;
354 }
355
set_complete_cb(CompleteCb cb)356 void DNSResolver::set_complete_cb(CompleteCb cb) {
357 completeCb_ = std::move(cb);
358 }
359
get_complete_cb() const360 CompleteCb DNSResolver::get_complete_cb() const { return completeCb_; }
361
362 } // namespace shrpx
363