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