12c593315Sopenharmony_ci/*
22c593315Sopenharmony_ci * nghttp2 - HTTP/2 C Library
32c593315Sopenharmony_ci *
42c593315Sopenharmony_ci * Copyright (c) 2016 Tatsuhiro Tsujikawa
52c593315Sopenharmony_ci *
62c593315Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining
72c593315Sopenharmony_ci * a copy of this software and associated documentation files (the
82c593315Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
92c593315Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
102c593315Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to
112c593315Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
122c593315Sopenharmony_ci * the following conditions:
132c593315Sopenharmony_ci *
142c593315Sopenharmony_ci * The above copyright notice and this permission notice shall be
152c593315Sopenharmony_ci * included in all copies or substantial portions of the Software.
162c593315Sopenharmony_ci *
172c593315Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
182c593315Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
192c593315Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
202c593315Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
212c593315Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
222c593315Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
232c593315Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
242c593315Sopenharmony_ci */
252c593315Sopenharmony_ci#include "shrpx_dns_resolver.h"
262c593315Sopenharmony_ci
272c593315Sopenharmony_ci#include <cstring>
282c593315Sopenharmony_ci#include <sys/time.h>
292c593315Sopenharmony_ci
302c593315Sopenharmony_ci#include "shrpx_log.h"
312c593315Sopenharmony_ci#include "shrpx_connection.h"
322c593315Sopenharmony_ci#include "shrpx_config.h"
332c593315Sopenharmony_ci
342c593315Sopenharmony_cinamespace shrpx {
352c593315Sopenharmony_ci
362c593315Sopenharmony_cinamespace {
372c593315Sopenharmony_civoid sock_state_cb(void *data, int s, int read, int write) {
382c593315Sopenharmony_ci  auto resolv = static_cast<DNSResolver *>(data);
392c593315Sopenharmony_ci
402c593315Sopenharmony_ci  if (resolv->get_status(nullptr) != DNSResolverStatus::RUNNING) {
412c593315Sopenharmony_ci    return;
422c593315Sopenharmony_ci  }
432c593315Sopenharmony_ci
442c593315Sopenharmony_ci  if (read) {
452c593315Sopenharmony_ci    resolv->start_rev(s);
462c593315Sopenharmony_ci  } else {
472c593315Sopenharmony_ci    resolv->stop_rev(s);
482c593315Sopenharmony_ci  }
492c593315Sopenharmony_ci  if (write) {
502c593315Sopenharmony_ci    resolv->start_wev(s);
512c593315Sopenharmony_ci  } else {
522c593315Sopenharmony_ci    resolv->stop_wev(s);
532c593315Sopenharmony_ci  }
542c593315Sopenharmony_ci}
552c593315Sopenharmony_ci} // namespace
562c593315Sopenharmony_ci
572c593315Sopenharmony_cinamespace {
582c593315Sopenharmony_civoid host_cb(void *arg, int status, int timeouts, hostent *hostent) {
592c593315Sopenharmony_ci  auto resolv = static_cast<DNSResolver *>(arg);
602c593315Sopenharmony_ci  resolv->on_result(status, hostent);
612c593315Sopenharmony_ci}
622c593315Sopenharmony_ci} // namespace
632c593315Sopenharmony_ci
642c593315Sopenharmony_cinamespace {
652c593315Sopenharmony_civoid process_result(DNSResolver *resolv) {
662c593315Sopenharmony_ci  auto cb = resolv->get_complete_cb();
672c593315Sopenharmony_ci  if (!cb) {
682c593315Sopenharmony_ci    return;
692c593315Sopenharmony_ci  }
702c593315Sopenharmony_ci  Address result;
712c593315Sopenharmony_ci  auto status = resolv->get_status(&result);
722c593315Sopenharmony_ci  switch (status) {
732c593315Sopenharmony_ci  case DNSResolverStatus::OK:
742c593315Sopenharmony_ci  case DNSResolverStatus::ERROR:
752c593315Sopenharmony_ci    cb(status, &result);
762c593315Sopenharmony_ci    break;
772c593315Sopenharmony_ci  default:
782c593315Sopenharmony_ci    break;
792c593315Sopenharmony_ci  }
802c593315Sopenharmony_ci  // resolv may be deleted here.
812c593315Sopenharmony_ci}
822c593315Sopenharmony_ci} // namespace
832c593315Sopenharmony_ci
842c593315Sopenharmony_cinamespace {
852c593315Sopenharmony_civoid readcb(struct ev_loop *loop, ev_io *w, int revents) {
862c593315Sopenharmony_ci  auto resolv = static_cast<DNSResolver *>(w->data);
872c593315Sopenharmony_ci  resolv->on_read(w->fd);
882c593315Sopenharmony_ci  process_result(resolv);
892c593315Sopenharmony_ci}
902c593315Sopenharmony_ci} // namespace
912c593315Sopenharmony_ci
922c593315Sopenharmony_cinamespace {
932c593315Sopenharmony_civoid writecb(struct ev_loop *loop, ev_io *w, int revents) {
942c593315Sopenharmony_ci  auto resolv = static_cast<DNSResolver *>(w->data);
952c593315Sopenharmony_ci  resolv->on_write(w->fd);
962c593315Sopenharmony_ci  process_result(resolv);
972c593315Sopenharmony_ci}
982c593315Sopenharmony_ci} // namespace
992c593315Sopenharmony_ci
1002c593315Sopenharmony_cinamespace {
1012c593315Sopenharmony_civoid timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
1022c593315Sopenharmony_ci  auto resolv = static_cast<DNSResolver *>(w->data);
1032c593315Sopenharmony_ci  resolv->on_timeout();
1042c593315Sopenharmony_ci  process_result(resolv);
1052c593315Sopenharmony_ci}
1062c593315Sopenharmony_ci} // namespace
1072c593315Sopenharmony_ci
1082c593315Sopenharmony_cinamespace {
1092c593315Sopenharmony_civoid stop_ev(struct ev_loop *loop,
1102c593315Sopenharmony_ci             const std::vector<std::unique_ptr<ev_io>> &evs) {
1112c593315Sopenharmony_ci  for (auto &w : evs) {
1122c593315Sopenharmony_ci    ev_io_stop(loop, w.get());
1132c593315Sopenharmony_ci  }
1142c593315Sopenharmony_ci}
1152c593315Sopenharmony_ci} // namespace
1162c593315Sopenharmony_ci
1172c593315Sopenharmony_ciDNSResolver::DNSResolver(struct ev_loop *loop)
1182c593315Sopenharmony_ci    : result_{},
1192c593315Sopenharmony_ci      loop_(loop),
1202c593315Sopenharmony_ci      channel_(nullptr),
1212c593315Sopenharmony_ci      family_(AF_UNSPEC),
1222c593315Sopenharmony_ci      status_(DNSResolverStatus::IDLE) {
1232c593315Sopenharmony_ci  ev_timer_init(&timer_, timeoutcb, 0., 0.);
1242c593315Sopenharmony_ci  timer_.data = this;
1252c593315Sopenharmony_ci}
1262c593315Sopenharmony_ci
1272c593315Sopenharmony_ciDNSResolver::~DNSResolver() {
1282c593315Sopenharmony_ci  if (channel_) {
1292c593315Sopenharmony_ci    ares_destroy(channel_);
1302c593315Sopenharmony_ci  }
1312c593315Sopenharmony_ci
1322c593315Sopenharmony_ci  stop_ev(loop_, revs_);
1332c593315Sopenharmony_ci  stop_ev(loop_, wevs_);
1342c593315Sopenharmony_ci
1352c593315Sopenharmony_ci  ev_timer_stop(loop_, &timer_);
1362c593315Sopenharmony_ci}
1372c593315Sopenharmony_ci
1382c593315Sopenharmony_ciint DNSResolver::resolve(const StringRef &name, int family) {
1392c593315Sopenharmony_ci  if (status_ != DNSResolverStatus::IDLE) {
1402c593315Sopenharmony_ci    return -1;
1412c593315Sopenharmony_ci  }
1422c593315Sopenharmony_ci
1432c593315Sopenharmony_ci  if (LOG_ENABLED(INFO)) {
1442c593315Sopenharmony_ci    LOG(INFO) << "Start resolving host " << name << " in IPv"
1452c593315Sopenharmony_ci              << (family == AF_INET ? "4" : "6");
1462c593315Sopenharmony_ci  }
1472c593315Sopenharmony_ci
1482c593315Sopenharmony_ci  name_ = name;
1492c593315Sopenharmony_ci  family_ = family;
1502c593315Sopenharmony_ci
1512c593315Sopenharmony_ci  int rv;
1522c593315Sopenharmony_ci
1532c593315Sopenharmony_ci  auto &dnsconf = get_config()->dns;
1542c593315Sopenharmony_ci
1552c593315Sopenharmony_ci  ares_options opts{};
1562c593315Sopenharmony_ci  opts.sock_state_cb = sock_state_cb;
1572c593315Sopenharmony_ci  opts.sock_state_cb_data = this;
1582c593315Sopenharmony_ci  opts.timeout = static_cast<int>(dnsconf.timeout.lookup * 1000);
1592c593315Sopenharmony_ci  opts.tries = dnsconf.max_try;
1602c593315Sopenharmony_ci
1612c593315Sopenharmony_ci  auto optmask = ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUTMS | ARES_OPT_TRIES;
1622c593315Sopenharmony_ci
1632c593315Sopenharmony_ci  ares_channel chan;
1642c593315Sopenharmony_ci  rv = ares_init_options(&chan, &opts, optmask);
1652c593315Sopenharmony_ci  if (rv != ARES_SUCCESS) {
1662c593315Sopenharmony_ci    if (LOG_ENABLED(INFO)) {
1672c593315Sopenharmony_ci      LOG(INFO) << "ares_init_options failed: " << ares_strerror(rv);
1682c593315Sopenharmony_ci    }
1692c593315Sopenharmony_ci    status_ = DNSResolverStatus::ERROR;
1702c593315Sopenharmony_ci    return -1;
1712c593315Sopenharmony_ci  }
1722c593315Sopenharmony_ci
1732c593315Sopenharmony_ci  channel_ = chan;
1742c593315Sopenharmony_ci  status_ = DNSResolverStatus::RUNNING;
1752c593315Sopenharmony_ci
1762c593315Sopenharmony_ci  ares_gethostbyname(channel_, name_.c_str(), family_, host_cb, this);
1772c593315Sopenharmony_ci  reset_timeout();
1782c593315Sopenharmony_ci
1792c593315Sopenharmony_ci  return 0;
1802c593315Sopenharmony_ci}
1812c593315Sopenharmony_ci
1822c593315Sopenharmony_ciint DNSResolver::on_read(int fd) { return handle_event(fd, ARES_SOCKET_BAD); }
1832c593315Sopenharmony_ci
1842c593315Sopenharmony_ciint DNSResolver::on_write(int fd) { return handle_event(ARES_SOCKET_BAD, fd); }
1852c593315Sopenharmony_ci
1862c593315Sopenharmony_ciint DNSResolver::on_timeout() {
1872c593315Sopenharmony_ci  return handle_event(ARES_SOCKET_BAD, ARES_SOCKET_BAD);
1882c593315Sopenharmony_ci}
1892c593315Sopenharmony_ci
1902c593315Sopenharmony_ciint DNSResolver::handle_event(int rfd, int wfd) {
1912c593315Sopenharmony_ci  if (status_ == DNSResolverStatus::IDLE) {
1922c593315Sopenharmony_ci    return -1;
1932c593315Sopenharmony_ci  }
1942c593315Sopenharmony_ci
1952c593315Sopenharmony_ci  ares_process_fd(channel_, rfd, wfd);
1962c593315Sopenharmony_ci
1972c593315Sopenharmony_ci  switch (status_) {
1982c593315Sopenharmony_ci  case DNSResolverStatus::RUNNING:
1992c593315Sopenharmony_ci    reset_timeout();
2002c593315Sopenharmony_ci    return 0;
2012c593315Sopenharmony_ci  case DNSResolverStatus::OK:
2022c593315Sopenharmony_ci    return 0;
2032c593315Sopenharmony_ci  case DNSResolverStatus::ERROR:
2042c593315Sopenharmony_ci    return -1;
2052c593315Sopenharmony_ci  default:
2062c593315Sopenharmony_ci    // Unreachable
2072c593315Sopenharmony_ci    assert(0);
2082c593315Sopenharmony_ci    abort();
2092c593315Sopenharmony_ci  }
2102c593315Sopenharmony_ci}
2112c593315Sopenharmony_ci
2122c593315Sopenharmony_civoid DNSResolver::reset_timeout() {
2132c593315Sopenharmony_ci  if (status_ != DNSResolverStatus::RUNNING) {
2142c593315Sopenharmony_ci    return;
2152c593315Sopenharmony_ci  }
2162c593315Sopenharmony_ci  timeval tvout;
2172c593315Sopenharmony_ci  auto tv = ares_timeout(channel_, nullptr, &tvout);
2182c593315Sopenharmony_ci  if (tv == nullptr) {
2192c593315Sopenharmony_ci    return;
2202c593315Sopenharmony_ci  }
2212c593315Sopenharmony_ci  // To avoid that timer_.repeat becomes 0, which makes ev_timer_again
2222c593315Sopenharmony_ci  // useless, add tiny fraction of time.
2232c593315Sopenharmony_ci  timer_.repeat = tv->tv_sec + tv->tv_usec / 1000000. + 1e-9;
2242c593315Sopenharmony_ci  ev_timer_again(loop_, &timer_);
2252c593315Sopenharmony_ci}
2262c593315Sopenharmony_ci
2272c593315Sopenharmony_ciDNSResolverStatus DNSResolver::get_status(Address *result) const {
2282c593315Sopenharmony_ci  if (status_ != DNSResolverStatus::OK) {
2292c593315Sopenharmony_ci    return status_;
2302c593315Sopenharmony_ci  }
2312c593315Sopenharmony_ci
2322c593315Sopenharmony_ci  if (result) {
2332c593315Sopenharmony_ci    memcpy(result, &result_, sizeof(result_));
2342c593315Sopenharmony_ci  }
2352c593315Sopenharmony_ci
2362c593315Sopenharmony_ci  return status_;
2372c593315Sopenharmony_ci}
2382c593315Sopenharmony_ci
2392c593315Sopenharmony_cinamespace {
2402c593315Sopenharmony_civoid start_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
2412c593315Sopenharmony_ci              int fd, int event, IOCb cb, void *data) {
2422c593315Sopenharmony_ci  for (auto &w : evs) {
2432c593315Sopenharmony_ci    if (w->fd == fd) {
2442c593315Sopenharmony_ci      return;
2452c593315Sopenharmony_ci    }
2462c593315Sopenharmony_ci  }
2472c593315Sopenharmony_ci  for (auto &w : evs) {
2482c593315Sopenharmony_ci    if (w->fd == -1) {
2492c593315Sopenharmony_ci      ev_io_set(w.get(), fd, event);
2502c593315Sopenharmony_ci      ev_io_start(loop, w.get());
2512c593315Sopenharmony_ci      return;
2522c593315Sopenharmony_ci    }
2532c593315Sopenharmony_ci  }
2542c593315Sopenharmony_ci
2552c593315Sopenharmony_ci  auto w = std::make_unique<ev_io>();
2562c593315Sopenharmony_ci  ev_io_init(w.get(), cb, fd, event);
2572c593315Sopenharmony_ci  w->data = data;
2582c593315Sopenharmony_ci  ev_io_start(loop, w.get());
2592c593315Sopenharmony_ci  evs.emplace_back(std::move(w));
2602c593315Sopenharmony_ci}
2612c593315Sopenharmony_ci} // namespace
2622c593315Sopenharmony_ci
2632c593315Sopenharmony_cinamespace {
2642c593315Sopenharmony_civoid stop_ev(std::vector<std::unique_ptr<ev_io>> &evs, struct ev_loop *loop,
2652c593315Sopenharmony_ci             int fd, int event) {
2662c593315Sopenharmony_ci  for (auto &w : evs) {
2672c593315Sopenharmony_ci    if (w->fd == fd) {
2682c593315Sopenharmony_ci      ev_io_stop(loop, w.get());
2692c593315Sopenharmony_ci      ev_io_set(w.get(), -1, event);
2702c593315Sopenharmony_ci      return;
2712c593315Sopenharmony_ci    }
2722c593315Sopenharmony_ci  }
2732c593315Sopenharmony_ci}
2742c593315Sopenharmony_ci} // namespace
2752c593315Sopenharmony_ci
2762c593315Sopenharmony_civoid DNSResolver::start_rev(int fd) {
2772c593315Sopenharmony_ci  start_ev(revs_, loop_, fd, EV_READ, readcb, this);
2782c593315Sopenharmony_ci}
2792c593315Sopenharmony_ci
2802c593315Sopenharmony_civoid DNSResolver::stop_rev(int fd) { stop_ev(revs_, loop_, fd, EV_READ); }
2812c593315Sopenharmony_ci
2822c593315Sopenharmony_civoid DNSResolver::start_wev(int fd) {
2832c593315Sopenharmony_ci  start_ev(wevs_, loop_, fd, EV_WRITE, writecb, this);
2842c593315Sopenharmony_ci}
2852c593315Sopenharmony_ci
2862c593315Sopenharmony_civoid DNSResolver::stop_wev(int fd) { stop_ev(wevs_, loop_, fd, EV_WRITE); }
2872c593315Sopenharmony_ci
2882c593315Sopenharmony_civoid DNSResolver::on_result(int status, hostent *hostent) {
2892c593315Sopenharmony_ci  stop_ev(loop_, revs_);
2902c593315Sopenharmony_ci  stop_ev(loop_, wevs_);
2912c593315Sopenharmony_ci  ev_timer_stop(loop_, &timer_);
2922c593315Sopenharmony_ci
2932c593315Sopenharmony_ci  if (status != ARES_SUCCESS) {
2942c593315Sopenharmony_ci    if (LOG_ENABLED(INFO)) {
2952c593315Sopenharmony_ci      LOG(INFO) << "Name lookup for " << name_
2962c593315Sopenharmony_ci                << " failed: " << ares_strerror(status);
2972c593315Sopenharmony_ci    }
2982c593315Sopenharmony_ci    status_ = DNSResolverStatus::ERROR;
2992c593315Sopenharmony_ci    return;
3002c593315Sopenharmony_ci  }
3012c593315Sopenharmony_ci
3022c593315Sopenharmony_ci  auto ap = *hostent->h_addr_list;
3032c593315Sopenharmony_ci  if (!ap) {
3042c593315Sopenharmony_ci    if (LOG_ENABLED(INFO)) {
3052c593315Sopenharmony_ci      LOG(INFO) << "Name lookup for " << name_ << "failed: no address returned";
3062c593315Sopenharmony_ci    }
3072c593315Sopenharmony_ci    status_ = DNSResolverStatus::ERROR;
3082c593315Sopenharmony_ci    return;
3092c593315Sopenharmony_ci  }
3102c593315Sopenharmony_ci
3112c593315Sopenharmony_ci  switch (hostent->h_addrtype) {
3122c593315Sopenharmony_ci  case AF_INET:
3132c593315Sopenharmony_ci    status_ = DNSResolverStatus::OK;
3142c593315Sopenharmony_ci    result_.len = sizeof(result_.su.in);
3152c593315Sopenharmony_ci    result_.su.in = {};
3162c593315Sopenharmony_ci    result_.su.in.sin_family = AF_INET;
3172c593315Sopenharmony_ci#ifdef HAVE_SOCKADDR_IN_SIN_LEN
3182c593315Sopenharmony_ci    result_.su.in.sin_len = sizeof(result_.su.in);
3192c593315Sopenharmony_ci#endif // HAVE_SOCKADDR_IN_SIN_LEN
3202c593315Sopenharmony_ci    memcpy(&result_.su.in.sin_addr, ap, sizeof(result_.su.in.sin_addr));
3212c593315Sopenharmony_ci    break;
3222c593315Sopenharmony_ci  case AF_INET6:
3232c593315Sopenharmony_ci    status_ = DNSResolverStatus::OK;
3242c593315Sopenharmony_ci    result_.len = sizeof(result_.su.in6);
3252c593315Sopenharmony_ci    result_.su.in6 = {};
3262c593315Sopenharmony_ci    result_.su.in6.sin6_family = AF_INET6;
3272c593315Sopenharmony_ci#ifdef HAVE_SOCKADDR_IN6_SIN6_LEN
3282c593315Sopenharmony_ci    result_.su.in6.sin6_len = sizeof(result_.su.in6);
3292c593315Sopenharmony_ci#endif // HAVE_SOCKADDR_IN6_SIN6_LEN
3302c593315Sopenharmony_ci    memcpy(&result_.su.in6.sin6_addr, ap, sizeof(result_.su.in6.sin6_addr));
3312c593315Sopenharmony_ci    break;
3322c593315Sopenharmony_ci  default:
3332c593315Sopenharmony_ci    assert(0);
3342c593315Sopenharmony_ci  }
3352c593315Sopenharmony_ci
3362c593315Sopenharmony_ci  if (status_ == DNSResolverStatus::OK) {
3372c593315Sopenharmony_ci    if (LOG_ENABLED(INFO)) {
3382c593315Sopenharmony_ci      LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
3392c593315Sopenharmony_ci                << util::numeric_name(&result_.su.sa, result_.len);
3402c593315Sopenharmony_ci    }
3412c593315Sopenharmony_ci    return;
3422c593315Sopenharmony_ci  }
3432c593315Sopenharmony_ci
3442c593315Sopenharmony_ci  status_ = DNSResolverStatus::ERROR;
3452c593315Sopenharmony_ci}
3462c593315Sopenharmony_ci
3472c593315Sopenharmony_civoid DNSResolver::set_complete_cb(CompleteCb cb) {
3482c593315Sopenharmony_ci  completeCb_ = std::move(cb);
3492c593315Sopenharmony_ci}
3502c593315Sopenharmony_ci
3512c593315Sopenharmony_ciCompleteCb DNSResolver::get_complete_cb() const { return completeCb_; }
3522c593315Sopenharmony_ci
3532c593315Sopenharmony_ci} // namespace shrpx
354