• 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 {
host_cb(void * arg,int status,int timeouts,hostent * hostent)58 void host_cb(void *arg, int status, int timeouts, hostent *hostent) {
59   auto resolv = static_cast<DNSResolver *>(arg);
60   resolv->on_result(status, hostent);
61 }
62 } // namespace
63 
64 namespace {
process_result(DNSResolver * resolv)65 void process_result(DNSResolver *resolv) {
66   auto cb = resolv->get_complete_cb();
67   if (!cb) {
68     return;
69   }
70   Address result;
71   auto status = resolv->get_status(&result);
72   switch (status) {
73   case DNSResolverStatus::OK:
74   case DNSResolverStatus::ERROR:
75     cb(status, &result);
76     break;
77   default:
78     break;
79   }
80   // resolv may be deleted here.
81 }
82 } // namespace
83 
84 namespace {
readcb(struct ev_loop * loop,ev_io * w,int revents)85 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
86   auto resolv = static_cast<DNSResolver *>(w->data);
87   resolv->on_read(w->fd);
88   process_result(resolv);
89 }
90 } // namespace
91 
92 namespace {
writecb(struct ev_loop * loop,ev_io * w,int revents)93 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
94   auto resolv = static_cast<DNSResolver *>(w->data);
95   resolv->on_write(w->fd);
96   process_result(resolv);
97 }
98 } // namespace
99 
100 namespace {
timeoutcb(struct ev_loop * loop,ev_timer * w,int revents)101 void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
102   auto resolv = static_cast<DNSResolver *>(w->data);
103   resolv->on_timeout();
104   process_result(resolv);
105 }
106 } // namespace
107 
108 namespace {
stop_ev(struct ev_loop * loop,const std::vector<std::unique_ptr<ev_io>> & evs)109 void stop_ev(struct ev_loop *loop,
110              const std::vector<std::unique_ptr<ev_io>> &evs) {
111   for (auto &w : evs) {
112     ev_io_stop(loop, w.get());
113   }
114 }
115 } // namespace
116 
DNSResolver(struct ev_loop * loop)117 DNSResolver::DNSResolver(struct ev_loop *loop)
118     : result_{},
119       loop_(loop),
120       channel_(nullptr),
121       family_(AF_UNSPEC),
122       status_(DNSResolverStatus::IDLE) {
123   ev_timer_init(&timer_, timeoutcb, 0., 0.);
124   timer_.data = this;
125 }
126 
~DNSResolver()127 DNSResolver::~DNSResolver() {
128   if (channel_) {
129     ares_destroy(channel_);
130   }
131 
132   stop_ev(loop_, revs_);
133   stop_ev(loop_, wevs_);
134 
135   ev_timer_stop(loop_, &timer_);
136 }
137 
resolve(const StringRef & name,int family)138 int DNSResolver::resolve(const StringRef &name, int family) {
139   if (status_ != DNSResolverStatus::IDLE) {
140     return -1;
141   }
142 
143   if (LOG_ENABLED(INFO)) {
144     LOG(INFO) << "Start resolving host " << name << " in IPv"
145               << (family == AF_INET ? "4" : "6");
146   }
147 
148   name_ = name;
149   family_ = family;
150 
151   int rv;
152 
153   auto &dnsconf = get_config()->dns;
154 
155   ares_options opts{};
156   opts.sock_state_cb = sock_state_cb;
157   opts.sock_state_cb_data = this;
158   opts.timeout = static_cast<int>(dnsconf.timeout.lookup * 1000);
159   opts.tries = dnsconf.max_try;
160 
161   auto optmask = ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
162 
163   ares_channel chan;
164   rv = ares_init_options(&chan, &opts, optmask);
165   if (rv != ARES_SUCCESS) {
166     if (LOG_ENABLED(INFO)) {
167       LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv);
168     }
169     status_ = DNSResolverStatus::ERROR;
170     return -1;
171   }
172 
173   channel_ = chan;
174   status_ = DNSResolverStatus::RUNNING;
175 
176   ares_gethostbyname(channel_, name_.c_str(), family_, host_cb, this);
177   reset_timeout();
178 
179   return 0;
180 }
181 
on_read(int fd)182 int DNSResolver::on_read(int fd) { return handle_event(fd, ARES_SOCKET_BAD); }
183 
on_write(int fd)184 int DNSResolver::on_write(int fd) { return handle_event(ARES_SOCKET_BAD, fd); }
185 
on_timeout()186 int DNSResolver::on_timeout() {
187   return handle_event(ARES_SOCKET_BAD, ARES_SOCKET_BAD);
188 }
189 
handle_event(int rfd,int wfd)190 int DNSResolver::handle_event(int rfd, int wfd) {
191   if (status_ == DNSResolverStatus::IDLE) {
192     return -1;
193   }
194 
195   ares_process_fd(channel_, rfd, wfd);
196 
197   switch (status_) {
198   case DNSResolverStatus::RUNNING:
199     reset_timeout();
200     return 0;
201   case DNSResolverStatus::OK:
202     return 0;
203   case DNSResolverStatus::ERROR:
204     return -1;
205   default:
206     // Unreachable
207     assert(0);
208     abort();
209   }
210 }
211 
reset_timeout()212 void DNSResolver::reset_timeout() {
213   if (status_ != DNSResolverStatus::RUNNING) {
214     return;
215   }
216   timeval tvout;
217   auto tv = ares_timeout(channel_, nullptr, &tvout);
218   if (tv == nullptr) {
219     return;
220   }
221   // To avoid that timer_.repeat becomes 0, which makes ev_timer_again
222   // useless, add tiny fraction of time.
223   timer_.repeat = tv->tv_sec + tv->tv_usec / 1000000. + 1e-9;
224   ev_timer_again(loop_, &timer_);
225 }
226 
get_status(Address * result) const227 DNSResolverStatus DNSResolver::get_status(Address *result) const {
228   if (status_ != DNSResolverStatus::OK) {
229     return status_;
230   }
231 
232   if (result) {
233     memcpy(result, &result_, sizeof(result_));
234   }
235 
236   return status_;
237 }
238 
239 namespace {
start_ev(std::vector<std::unique_ptr<ev_io>> & evs,struct ev_loop * loop,int fd,int event,IOCb cb,void * data)240 void start_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
241               int fd, int event, IOCb cb, void *data) {
242   for (auto &w : evs) {
243     if (w->fd == fd) {
244       return;
245     }
246   }
247   for (auto &w : evs) {
248     if (w->fd == -1) {
249       ev_io_set(w.get(), fd, event);
250       ev_io_start(loop, w.get());
251       return;
252     }
253   }
254 
255   auto w = std::make_unique<ev_io>();
256   ev_io_init(w.get(), cb, fd, event);
257   w->data = data;
258   ev_io_start(loop, w.get());
259   evs.emplace_back(std::move(w));
260 }
261 } // namespace
262 
263 namespace {
stop_ev(std::vector<std::unique_ptr<ev_io>> & evs,struct ev_loop * loop,int fd,int event)264 void stop_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
265              int fd, int event) {
266   for (auto &w : evs) {
267     if (w->fd == fd) {
268       ev_io_stop(loop, w.get());
269       ev_io_set(w.get(), -1, event);
270       return;
271     }
272   }
273 }
274 } // namespace
275 
start_rev(int fd)276 void DNSResolver::start_rev(int fd) {
277   start_ev(revs_, loop_, fd, EV_READ, readcb, this);
278 }
279 
stop_rev(int fd)280 void DNSResolver::stop_rev(int fd) { stop_ev(revs_, loop_, fd, EV_READ); }
281 
start_wev(int fd)282 void DNSResolver::start_wev(int fd) {
283   start_ev(wevs_, loop_, fd, EV_WRITE, writecb, this);
284 }
285 
stop_wev(int fd)286 void DNSResolver::stop_wev(int fd) { stop_ev(wevs_, loop_, fd, EV_WRITE); }
287 
on_result(int status,hostent * hostent)288 void DNSResolver::on_result(int status, hostent *hostent) {
289   stop_ev(loop_, revs_);
290   stop_ev(loop_, wevs_);
291   ev_timer_stop(loop_, &timer_);
292 
293   if (status != ARES_SUCCESS) {
294     if (LOG_ENABLED(INFO)) {
295       LOG(INFO) << "Name lookup for " << name_
296                 << " failed: " << ares_strerror(status);
297     }
298     status_ = DNSResolverStatus::ERROR;
299     return;
300   }
301 
302   auto ap = *hostent->h_addr_list;
303   if (!ap) {
304     if (LOG_ENABLED(INFO)) {
305       LOG(INFO) << "Name lookup for " << name_ << "failed: no address returned";
306     }
307     status_ = DNSResolverStatus::ERROR;
308     return;
309   }
310 
311   switch (hostent->h_addrtype) {
312   case AF_INET:
313     status_ = DNSResolverStatus::OK;
314     result_.len = sizeof(result_.su.in);
315     result_.su.in = {};
316     result_.su.in.sin_family = AF_INET;
317 #ifdef HAVE_SOCKADDR_IN_SIN_LEN
318     result_.su.in.sin_len = sizeof(result_.su.in);
319 #endif // HAVE_SOCKADDR_IN_SIN_LEN
320     memcpy(&result_.su.in.sin_addr, ap, sizeof(result_.su.in.sin_addr));
321     break;
322   case AF_INET6:
323     status_ = DNSResolverStatus::OK;
324     result_.len = sizeof(result_.su.in6);
325     result_.su.in6 = {};
326     result_.su.in6.sin6_family = AF_INET6;
327 #ifdef HAVE_SOCKADDR_IN6_SIN6_LEN
328     result_.su.in6.sin6_len = sizeof(result_.su.in6);
329 #endif // HAVE_SOCKADDR_IN6_SIN6_LEN
330     memcpy(&result_.su.in6.sin6_addr, ap, sizeof(result_.su.in6.sin6_addr));
331     break;
332   default:
333     assert(0);
334   }
335 
336   if (status_ == DNSResolverStatus::OK) {
337     if (LOG_ENABLED(INFO)) {
338       LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
339                 << util::numeric_name(&result_.su.sa, result_.len);
340     }
341     return;
342   }
343 
344   status_ = DNSResolverStatus::ERROR;
345 }
346 
set_complete_cb(CompleteCb cb)347 void DNSResolver::set_complete_cb(CompleteCb cb) {
348   completeCb_ = std::move(cb);
349 }
350 
get_complete_cb() const351 CompleteCb DNSResolver::get_complete_cb() const { return completeCb_; }
352 
353 } // namespace shrpx
354