1 /***************************************************************************
2 * *
3 * This program is free software; you can redistribute it and/or modify *
4 * it under the terms of the GNU General Public License as published by *
5 * the Free Software Foundation; either version 2 of the License, or *
6 * (at your option) any later version. *
7 * *
8 ***************************************************************************
9 *
10 * net/resolver.cc
11 * (c) 2005-2008 Murat Deligonul
12 */
13
14 #include "autoconf.h"
15
16 #include <algorithm>
17 #include <iterator>
18 #include <csignal>
19 #include <cstdio>
20 #include <cstring>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #ifdef HAVE_SYS_FILIO_H
26 # include <sys/filio.h>
27 #endif
28 #ifdef HAVE_SYS_IOCTL_H
29 # include <sys/ioctl.h>
30 #endif
31 #include "util/strings.h"
32 #include "net/resolver.h"
33
34 #include "debug.h"
35
36 namespace net {
37
38 using namespace util::strings;
39
40 /* static */ const struct resolver::resolver_thread
41 resolver::default_rt = { 0, 0 };
42
43
resolver(unsigned max_threads,unsigned max_lookups)44 resolver::resolver(unsigned max_threads, unsigned max_lookups)
45 : MAX_THREADS(max_threads),
46 MAX_LOOKUPS_PER_THREAD(max_lookups),
47 current_id(0),
48 active_threads(0),
49 first_thread(default_rt)
50 {
51 assert(max_threads > 0);
52 assert(max_lookups > 0);
53
54 /**
55 * Set up:
56 * -- FIFO for relaying results
57 * -- mutex and condition variables
58 */
59 if (pipe(fifo) < 0) {
60 // FIXME: complain
61 abort();
62 }
63
64 pthread_mutex_init(&queue_mutex, 0);
65 pthread_mutex_init(&threads_mutex, 0);
66 pthread_cond_init(&queue_cv, 0);
67
68 DEBUG("resolver::resolver() : [%p] READY w/ fifo: [%d:%d] max threads: %d max lpt: %d\n",
69 this, fifo[0], fifo[1], max_threads, max_lookups);
70 }
71
72 /* Destructor */
~resolver()73 resolver::~resolver()
74 {
75 if (first_thread.self) {
76 pthread_kill(first_thread.thread, SIGKILL);
77 }
78 pthread_mutex_destroy(&queue_mutex);
79 pthread_mutex_destroy(&threads_mutex);
80 pthread_cond_destroy(&queue_cv);
81
82 close(fifo_reader_fd());
83 close(fifo_writer_fd());
84 }
85
create_request(int family,const char * hostname,unsigned short port,int options,const resolver_callback * callback)86 /* static */ resolver::request * resolver::create_request(int family,
87 const char * hostname,
88 unsigned short port,
89 int options,
90 const resolver_callback * callback)
91 {
92 struct request * req = new request;
93 req->id = 0; /* no ID is assigned until put into wait queue */
94 req->family = family;
95 req->options = options;
96 req->port = port;
97 req->ai = 0;
98 req->callback = callback;
99 my_strlcpy(req->name, hostname, sizeof(req->name));
100 return req;
101 }
102
103
104 /**
105 * Return ID of new async request on success.
106 * Otherwise return error code
107 **/
async_lookup(resolver::request * req)108 int resolver::async_lookup(resolver::request * req)
109 {
110 req->id = ++current_id;
111 /* we reach this point with a lookup ready to go */
112 pthread_mutex_lock(&queue_mutex);
113 request_queue.push(req);
114 unsigned int n = request_queue.size();
115 pthread_mutex_unlock(&queue_mutex);
116
117 /* determine if we have to dispatch a new thread */
118 if (active_threads < MAX_THREADS
119 && n > (active_threads * MAX_LOOKUPS_PER_THREAD))
120 {
121 resolver_thread * rt;
122 if (!first_thread.self)
123 rt = &first_thread;
124 else
125 rt = new resolver_thread;
126 rt->self = this;
127
128 pthread_attr_t attr;
129 pthread_attr_init(&attr);
130 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
131
132 pthread_mutex_lock(&threads_mutex);
133 ++active_threads;
134 pthread_mutex_unlock(&threads_mutex);
135
136 /* race condition ?? */
137 n = pthread_create(&rt->thread, &attr, resolver_thread_fn, rt);
138 pthread_attr_destroy(&attr);
139 }
140
141 pthread_cond_broadcast(&queue_cv);
142
143 return req->id;
144 }
145
146
147 /**
148 * Put a request in the cancellation queue to ignore
149 * the result of request 'i' when we come across it ..
150 */
cancel_async_lookup(int i)151 int resolver::cancel_async_lookup(int i)
152 {
153 cancelled_requests.push_back(i);
154 return cancelled_requests.size();
155 }
156
resolver_thread_fn(void * param)157 /* static */ void * resolver::resolver_thread_fn(void * param)
158 {
159 assert(param);
160 resolver_thread * rt = (resolver_thread *) param;
161 resolver * self = rt->self;
162 request * req = NULL;
163 bool first = (rt == &self->first_thread);
164
165 DEBUG("resolver::resolver_thread_fn() -- new thread\n");
166
167 pthread_mutex_lock(&self->queue_mutex);
168
169 while (first || self->request_queue.size() > self->MAX_LOOKUPS_PER_THREAD)
170 {
171 while (self->request_queue.empty()) {
172 pthread_cond_wait(&self->queue_cv, &self->queue_mutex);
173 }
174 req = self->request_queue.front();
175 self->request_queue.pop();
176 pthread_mutex_unlock(&self->queue_mutex);
177
178 DEBUG("resolver::resolver_thread_fn() -- processing request: %d\n", req->id);
179 /** perform the blocking lookup **/
180 self->process_request(req);
181 self->write_result(req);
182
183 pthread_mutex_lock(&self->queue_mutex);
184 }
185
186 pthread_mutex_unlock(&self->queue_mutex);
187
188 /* No more to do. If we're not the first thread, delete
189 * our data parameter and kill thread */
190 pthread_mutex_lock(&self->threads_mutex);
191 --self->active_threads;
192 pthread_mutex_unlock(&self->threads_mutex);
193
194 DEBUG("resolver::resolver_thread_fn(): Thread exiting\n");
195 if (!first) {
196 delete rt;
197 }
198 pthread_exit(0);
199 return 0; /* NOT REACHED */
200 }
201
202
process_request(resolver::request * req)203 int resolver::process_request(resolver::request * req)
204 {
205 if (req->options & OPT_REVERSE_LOOKUP) {
206 resolve_address(req->family, AI_NUMERICHOST, req->name, req->port, &req->ai);
207 req->iresult = reverse_lookup(req->name, req->name, sizeof(req->name));
208 }
209 else {
210 req->iresult = resolve_address(req->family, 0, req->name, req->port, &req->ai);
211 }
212 return req->iresult;
213 }
214
215 /**
216 * Write the result to FIFO to be read by main thread.
217 * Currently we are cheap and just write the pointer to the request
218 * data structure.
219 * This operation completes the work in the resolving thread.
220 */
write_result(const resolver::request * req)221 int resolver::write_result(const resolver::request * req)
222 {
223 return write(fifo_writer_fd(), &req, sizeof(resolver::request *));
224 }
225
226
227 /** Read async look-up results **/
process_results()228 int resolver::process_results()
229 {
230 int processed = 0;
231 int i = 0;
232 do {
233 // FIXME: why is this in the loop?
234 ioctl(fifo_reader_fd(), FIONREAD, &i);
235 if (unsigned(i) >= sizeof(struct request *)) {
236 struct request * req = read_result();
237 process_result(req);
238 ++processed;
239 /* at this point the request has resolved,
240 * the callback function notified, and any allocated
241 * resources for the request freed */
242 }
243 i -= sizeof(struct request *);
244 } while (i > 0 && unsigned(i) >= sizeof(struct request *));
245
246 return processed;
247 }
248
249 /** read an indiviual result from the FIFO */
read_result()250 resolver::request * resolver::read_result()
251 {
252 request * req = NULL;
253 read(fifo_reader_fd(), &req, sizeof(req));
254 return req;
255 }
256
process_result(request * req)257 int resolver::process_result(request * req)
258 {
259 assert(req);
260 assert(req->id > 0);
261
262 bool cancelled = false;
263
264 do {
265 /* First check if it's been cancelled */
266 std::vector<int>::iterator i =
267 std::find(cancelled_requests.begin(),
268 cancelled_requests.end(),
269 req->id);
270
271 if (i != cancelled_requests.end()) {
272 DEBUG("resolver::process_result(): CANCELLED: %d\n", req->id);
273 /* Must cancel this one */
274 cancelled_requests.erase(i);
275 cancelled = true;
276 continue;
277 }
278 } while (0); /* we must get rid of potential duplicate cancellation requests */
279
280 if (!cancelled) {
281 if (req->iresult == 0) {
282 req->callback->async_lookup_finished(req);
283 }
284 else {
285 req->callback->async_lookup_failed(req);
286 }
287 }
288
289 delete req;
290 return 0;
291 }
292
293
294 /**
295 * [BLOCKING] The unified address resolver.
296 * Translates a network address or hostname into socket address structures.
297 * Handles both IPv4 and IPv6 address families.
298 *
299 * @param family desired address family (or AF_UNSPEC)
300 * @param hint_flags hints or flags for the resolver (the addrinfo.ai_flags field)
301 * @param hostname host name to look up
302 * @param port port to fill address structures with
303 * @param res pointer to pointer to addrinfo data which will store the result
304 *
305 * @return [same as getaddrinfo()]
306 * 0: success; res populated
307 * <> 0: error; res untouched
308 */
resolve_address(int family,int hint_flags,const char * hostname,unsigned short port,struct addrinfo ** res)309 /* static */ int resolver::resolve_address(int family,
310 int hint_flags,
311 const char * hostname,
312 unsigned short port,
313 struct addrinfo ** res)
314 {
315 struct addrinfo hints;
316 char portbuf[6] = "";
317
318 memset(&hints, 0, sizeof(hints));
319 hints.ai_flags = hint_flags;
320 hints.ai_family = family;
321 hints.ai_socktype = SOCK_STREAM;
322 hints.ai_protocol = IPPROTO_TCP;
323
324 sprintf(portbuf, "%u", port);
325
326 return getaddrinfo(hostname, portbuf, &hints, res);
327 }
328
329 /**
330 * [BLOCKING]
331 * Simpler interface to resolve_address.
332 * Translates hostname to IP address.
333 *
334 * @param hostname name to lookup
335 * @param buffer buffer to store result in
336 * @param len length of buffer
337 * @return 0 for success
338 */
lookup(const char * hostname,char * buffer,size_t len)339 /* static */ int resolver::lookup(const char * hostname,
340 char * buffer,
341 size_t len)
342 {
343 struct addrinfo * ai = NULL;
344 int i = resolve_address(AF_UNSPEC, 0, hostname, 0, &ai);
345 if (i != 0) {
346 return i;
347 }
348
349 i = raw_to_ip(ai->ai_addr, ai->ai_addrlen, buffer, len);
350 freeaddrinfo(ai);
351 return i;
352 }
353
354
355 /**
356 * [BLOCKING]
357 * Attempt to translate IP -> hostname.
358 * @param name numeric address to translate
359 * @param buffer buffer to store result in
360 * @param len length of buffer
361 *
362 * @return 0 on success, otherwise getnameinfo() error code
363 */
reverse_lookup(const char * name,char * buffer,size_t len)364 /* static */ int resolver::reverse_lookup(const char * name,
365 char * buffer,
366 size_t len)
367 {
368 struct addrinfo * ai = NULL;
369 int i = resolve_address(AF_UNSPEC, AI_NUMERICHOST,
370 name, 0, &ai);
371 if (i != 0) {
372 return i;
373 }
374
375 i = getnameinfo(ai->ai_addr, ai->ai_addrlen, buffer, len,
376 NULL, 0,
377 0 /* NI_NAMEREQD */); // XXX
378 freeaddrinfo(ai);
379 return i;
380 }
381
382
383 /**
384 * Tests if binding to this interface will work.
385 *
386 * @param address address to bind on. May be NULL, in which case default interface will be used.
387 * @param port port to bind on. May be zero.
388 * @return 0 on success, -1 for name lookup related error, 1 for other errors
389 */
test_bind(const char * address,unsigned short port)390 /* static */ int resolver::test_bind(const char * address, unsigned short port)
391 {
392 int hints = AI_ADDRCONFIG;
393 if (address == NULL) {
394 hints |= AI_PASSIVE;
395 }
396 struct addrinfo * ai = NULL;
397 if (resolve_address(PF_UNSPEC, hints, address, port, &ai) != 0) {
398 return -1;
399 }
400
401 // create socket and test bind
402 int ret = 0;
403 int s = socket(ai->ai_family, SOCK_STREAM, 0);
404 if (s < 0 || bind(s, ai->ai_addr, ai->ai_addrlen) < 0) {
405 ret = 1;
406 }
407
408 // result?
409 close(s);
410 freeaddrinfo(ai);
411 return ret;
412 }
413
414 /**
415 * Translate a raw network address structure into a text representation.
416 *
417 * @param addr address structure
418 * @param addrlen size of address structure
419 * @param buff buffer to store result in
420 * @param bufflen length of buffer
421 * @param port pointer to unsigned short to store port; may be NULL
422 * @return 0 on success
423 */
raw_to_ip(const struct sockaddr * addr,size_t addrlen,char * buff,size_t bufflen,unsigned short * port)424 /* static */ int resolver::raw_to_ip(const struct sockaddr * addr,
425 size_t addrlen,
426 char * buff,
427 size_t bufflen,
428 unsigned short * port)
429 {
430 char temp[10];
431 unsigned short dummy;
432 if (port == NULL) {
433 port = &dummy;
434 }
435
436 int i = getnameinfo(addr, addrlen, buff, bufflen,
437 temp, sizeof temp,
438 NI_NUMERICHOST | NI_NUMERICSERV);
439 if (i != 0) {
440 return i;
441 }
442
443 // assign port
444 *port = static_cast<unsigned short>(atoi(temp));
445 return i;
446 }
447
448
449 /**
450 * Translate a numeric address string into a network address structure.
451 * The address family is determined by a call to getaddrinfo(). The provided
452 * buffer must be large enough to store the resulting address structure, or an
453 * error code will be returned.
454 *
455 * @param name address to string to translate
456 * @param port port to assign in address structure's field
457 * @param out structure to fill
458 * @param out_size size of the structure
459 * @return 0 on success, error code if address is invalid or buffer size is insufficient.
460 */
ip_to_raw(const char * name,unsigned short port,struct sockaddr * out,size_t out_size)461 int resolver::ip_to_raw(const char * name,
462 unsigned short port,
463 struct sockaddr * out,
464 size_t out_size)
465 {
466 struct addrinfo * ai = NULL;
467 int i = resolve_address(AF_UNSPEC, AI_NUMERICHOST,
468 name, port, &ai);
469 if (i != 0) {
470 return i;
471 }
472
473 // verify buffer space
474 if (ai->ai_addrlen > out_size) {
475 #ifdef EAI_OVERFLOW
476 // this might be Linux specific
477 i = EAI_OVERFLOW;
478 #else
479 i = -1;
480 #endif
481 }
482 else {
483 memset(out, 0, out_size);
484 memcpy(out, ai->ai_addr, ai->ai_addrlen);
485 }
486 freeaddrinfo(ai);
487 return i;
488 }
489
490 /**
491 * Determine whether a given address is a numeric IP address.
492 *
493 * @param af address family to check (can be AF_UNSPEC)
494 * @param name name to check
495 * @return true if ip address detected
496 */
is_ip_address(int af,const char * name)497 /* static */ bool resolver::is_ip_address(int af, const char * name)
498 {
499 struct sockaddr_storage dummy;
500
501 int ip4 = ip_to_raw(name, 0, (struct sockaddr *) &dummy, sizeof(struct sockaddr_in));
502 int ip6 = ip_to_raw(name, 0, (struct sockaddr *) &dummy, sizeof(struct sockaddr_in6));
503
504 if (af == AF_INET) {
505 return ip4 == 0;
506 }
507 else if (af == AF_INET6) {
508 return ip6 == 0;
509 }
510 return ip4 == 0 || ip6 == 0;
511 }
512
result_to_string(const resolver::request * req,char * buf,size_t len)513 /* static */ char * resolver::result_to_string(const resolver::request * req,
514 char * buf,
515 size_t len)
516 {
517 if (req->options & OPT_REVERSE_LOOKUP) {
518 my_strlcpy(buf, req->name, len);
519 }
520 else {
521 raw_to_ip(req->ai->ai_addr, req->ai->ai_addrlen, buf, len);
522 }
523 return buf;
524 }
525
526 } /* namespace net */
527