• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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