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