Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
resolver.cpp
1 
2 /***************************************************************************
3  * resolver.cpp - Fawkes network name resolver
4  *
5  * Created: Tue Nov 14 14:25:52 2006
6  * Copyright 2006-2009 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <netcomm/utils/resolver.h>
25 #include <netcomm/utils/resolver_thread.h>
26 #include <core/exceptions/system.h>
27 #include <core/threading/mutex_locker.h>
28 #include <utils/system/hostinfo.h>
29 
30 #include <sys/types.h>
31 #include <arpa/inet.h>
32 #include <netdb.h>
33 #include <netinet/in.h>
34 #include <cstring>
35 #include <cstdlib>
36 #include <cstdio>
37 #include <unistd.h>
38 
39 namespace fawkes {
40 #if 0 /* just to make Emacs auto-indent happy */
41 }
42 #endif
43 
44 /** @class NetworkNameResolver <netcomm/utils/resolver.h>
45  * Network name and address resolver.
46  * This class implements a facility to resolve host names to addresses
47  * and vice versa. It provides a simplified interface that only supports
48  * IPv4 and will return only the first answer received. It has
49  * optional support for using mDNS via Avahi for lookups on the local
50  * network in the .local domain.
51  *
52  * Quite some effort has been done to ensure the speediness of this
53  * implementation. It is assumed that a fast lookup is the most important
54  * thing for the resolver. This means especially that under some circumstances
55  * no result can be supplied on the first call but just on a subsequent call
56  * (it is not defined if or when a result will be available). So it is a
57  * good choice to always call the resolver and let it do the right thing
58  * and not cache names and addresses in your own application. This also
59  * makes the cache more efficient since multiple threads may use this
60  * single resolver. The resolver itself holds a resolver thread that
61  * will do lookups concurrently which will update the cache with
62  * results thus that subsequent calls will provide the correct
63  * information from the cache.
64  *
65  * The resolver uses an internal cache of name to address and addres to
66  * name mapping. If a valid lookup has happened this mapping is assumed
67  * to be authoritative and that it will not change. If you want to flush
68  * the cache from time to time you may use flush_cache() to do so.
69  *
70  * In general resolve_name() and resolve_address() immediately return. If no
71  * answer is in the cache for resolve_address() it will just provide the textual
72  * representation of the IP address. This is different for resolve_name(). If
73  * no answer is available if will order a concurrent lookup and return a
74  * lookup failure. Subsequent calls may succeed if the cache was successfully
75  * updated by the concurrent resolver thread. If you need the answer to be
76  * able to proceed use resolve_name_blocking(). This will wait until an
77  * answer is available via the host lookup facilities of the system or
78  * optional via mDNS.
79  *
80  * @ingroup NetComm
81  * @author Tim Niemueller
82  */
83 
84 
85 /** Constructor.
86  * This constructor us available if Avahi is available at compile time.
87  * @param avahi_thread Optional avahi thread, Avahi is not used if NULL
88  */
89 NetworkNameResolver::NetworkNameResolver(AvahiThread *avahi_thread)
90 {
91  addr2name_cache.clear();
92  name2addr_cache.clear();
93  __cache_timeout = 30;
94 
95  resolver_thread = new NetworkNameResolverThread(this, avahi_thread);
96  resolver_thread->start();
97  // Wait for thread to start
98  usleep(0);
99 
100  __host_info = new HostInfo();
101 }
102 
103 
104 /** Destructor. */
105 NetworkNameResolver::~NetworkNameResolver()
106 {
107  flush_cache();
108  resolver_thread->cancel();
109  resolver_thread->join();
110  delete resolver_thread;
111  delete __host_info;
112 }
113 
114 /** Set cache timeout.
115  * The apply only applies to consecutive lookups, existing entries will expire
116  * with the old timeout.
117  * @param sec the timeout in seconds determines after which time successful
118  * resolutions are purged from the cache.
119  */
120 void
121 NetworkNameResolver::set_cache_timeout(unsigned int sec)
122 {
123  __cache_timeout = sec;
124 }
125 
126 
127 /** Get cache timeout.
128  * @return resolution cache timeout in seconds
129  */
130 unsigned int
131 NetworkNameResolver::cache_timeout()
132 {
133  return __cache_timeout;
134 }
135 
136 
137 /** Flush cache.
138  * Flushes the caches for name to address and address to name mappings.
139  */
140 void
141 NetworkNameResolver::flush_cache()
142 {
143  addr2name_cache.lock();
144  while ( ! addr2name_cache.empty() ) {
145  a2ncit = addr2name_cache.begin();
146  free((*a2ncit).second.first);
147  addr2name_cache.erase(a2ncit);
148  }
149  addr2name_cache.unlock();
150  name2addr_cache.lock();
151  while ( ! name2addr_cache.empty() ) {
152  n2acit = name2addr_cache.begin();
153  char *name = (*n2acit).first;
154  free((*n2acit).second.first);
155  name2addr_cache.erase(n2acit);
156  free(name);
157  }
158  name2addr_cache.unlock();
159  __host_info->update();
160 
161  /* Leads to a segfault, if one element is in the queue it is deleted
162  * two times, do not use
163  for (n2acit = name2addr_cache.begin(); n2acit != name2addr_cache.end(); ++n2acit) {
164  free((*n2acit).first);
165  free((*n2acit).second.first);
166  }
167  */
168 }
169 
170 
171 /** Resolve name.
172  * This will lookup a name from the cache and return the value if available.
173  * If there is no entry in the cache this will order a concurrent lookup of the
174  * name an return a failure.
175  * @param name name to resolve
176  * @param addr contains a pointer to the address record upon return, this record
177  * is in the cache, so you may not free the resulting address! The address is
178  * always of type struct sockaddr_in (IPv4) at the moment.
179  * @param addrlen contains the length of addr in bytes upon return
180  * @return true if resolution was successful, false otherwise
181  */
182 bool
183 NetworkNameResolver::resolve_name(const char *name,
184  struct sockaddr **addr, socklen_t *addrlen)
185 {
186  name2addr_cache.lock();
187 
188  if ( name2addr_cache.find( (char *)name ) != name2addr_cache.end() ) {
189  // the name is in the cache, refetch?
190  std::pair<struct sockaddr *, time_t> &nrec = name2addr_cache[(char *)name];
191  if ( nrec.second <= time(NULL) ) {
192  // entry outdated, retry
193  resolver_thread->resolve_name(name);
194  }
195  *addr = nrec.first;
196  *addrlen = sizeof(struct sockaddr_in);
197  name2addr_cache.unlock();
198  return true;
199  } else {
200  name2addr_cache.unlock();
201  resolver_thread->resolve_name(name);
202  return false;
203  }
204 }
205 
206 
207 /** Resolve name and wait for the result.
208  * This will lookup a name from the cache and return the value if available.
209  * If there is no entry in the cache this will order a concurrent lookup of the
210  * name and wait for the result.
211  * @param name name to resolve
212  * @param addr contains a pointer to the address record upon return, this record
213  * is in the cache, so you may not free the resulting address! The address is
214  * always of type struct sockaddr_in (IPv4) at the moment.
215  * @param addrlen contains the length of addr in bytes upon return
216  * @return true if resolution was successful, false otherwise
217  */
218 bool
219 NetworkNameResolver::resolve_name_blocking(const char *name,
220  struct sockaddr **addr, socklen_t *addrlen)
221 {
222  if ( resolve_name(name, addr, addrlen) ) {
223  return true;
224  } else {
225  struct sockaddr *_addr;
226  socklen_t _addrlen;
227  if ( resolver_thread->resolve_name_immediately(name, &_addr, &_addrlen) ) {
228  name_resolved(strdup(name), _addr, _addrlen);
229  *addr = _addr;
230  *addrlen = _addrlen;
231  return true;
232  } else {
233  return false;
234  }
235  }
236 }
237 
238 
239 /** Resolve address.
240  * This will lookup an address from the cache and return the value if available.
241  * If there is no entry in the cache this will order a concurrent lookup of the
242  * address and return the textual representation of the address.
243  * @param addr address to resolve
244  * @param addr_len length of addr in bytes
245  * @param name contains a pointer to the name upon return. Note that this record
246  * resides in the cache and may not be freed.
247  * @return true if resolution was successful, false otherwise
248  */
249 bool
250 NetworkNameResolver::resolve_address(struct sockaddr *addr, socklen_t addr_len, std::string &name)
251 {
252  addr2name_cache.lock();
253  struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
254 
255  if ( addr2name_cache.find( saddr->sin_addr.s_addr ) != addr2name_cache.end() ) {
256  // the name is in the cache, refetch?
257  std::pair<char *, time_t> &nrec = addr2name_cache[saddr->sin_addr.s_addr];
258  name = nrec.first;
259  if ( nrec.second <= time(NULL) ) {
260  // entry outdated, retry
261  addr2name_cache.unlock();
262  resolver_thread->resolve_address(addr, addr_len);
263  } else {
264  addr2name_cache.unlock();
265  }
266  } else {
267  char tmp[INET_ADDRSTRLEN];
268  if ( inet_ntop(AF_INET, &(saddr->sin_addr), tmp, sizeof(tmp)) ) {
269  char *n = strdup(tmp);
270 
271  addr2name_cache[saddr->sin_addr.s_addr] = std::pair<char *, time_t>(n, time(NULL) + __cache_timeout);
272  name = n;
273  addr2name_cache.unlock();
274  } else {
275  addr2name_cache.unlock();
276  return false;
277  }
278 
279  resolver_thread->resolve_address(addr, addr_len);
280  }
281 
282  return true;
283 
284  /*
285  char hbuf[NI_MAXHOST];
286  if ( getnameinfo(addr, addr_len, hbuf, sizeof(hbuf), NULL, 0, 0) == -1 ) {
287  return false;
288  } else {
289  char *tmp = (char *)malloc(strlen(hbuf) + 1);
290  if ( ! tmp ) {
291  throw OutOfMemoryException();
292  }
293  strcpy(tmp, hbuf);
294  *name = tmp;
295  return true;
296  }
297  */
298 }
299 
300 
301 /** Name has been resolved by resolver thread.
302  * This is an internal function, if you modify it, please make absolutely sure that you
303  * understand the caches, especially when the key has to be freed! Also note that we
304  * take over the ownership name and addr and are responsible for freeing at some
305  * point!
306  * @param name host name
307  * @param addr address structure
308  * @param addrlen length in bytes of addr
309  */
310 void
311 NetworkNameResolver::name_resolved(char *name, struct sockaddr *addr,
312  socklen_t addrlen)
313 {
314  name2addr_cache.lock();
315  if ( (n2acit = name2addr_cache.find( name )) != name2addr_cache.end() ) {
316  // delete old entry
317  char *n = (*n2acit).first;
318  free((*n2acit).second.first);
319  name2addr_cache.erase(n2acit);
320  free(n);
321  }
322  name2addr_cache[name] = std::pair<struct sockaddr *, time_t>(addr, time(NULL) + __cache_timeout);
323  name2addr_cache.unlock();
324 }
325 
326 
327 void
328 NetworkNameResolver::addr_resolved(struct sockaddr *addr,
329  socklen_t addrlen,
330  char *name, bool namefound)
331 {
332  addr2name_cache.lock();
333  struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
334  if (namefound) {
335  if ((a2ncit = addr2name_cache.find( saddr->sin_addr.s_addr )) != addr2name_cache.end() ) {
336  // delete old entry
337  free(a2ncit->second.first);
338  addr2name_cache.erase(a2ncit);
339  addr2name_cache[saddr->sin_addr.s_addr] = std::pair<char *, time_t>(name, time(NULL) + __cache_timeout);
340  } else {
341  free(name);
342  }
343  } else {
344  if ((a2ncit = addr2name_cache.find( saddr->sin_addr.s_addr )) == addr2name_cache.end() ) {
345  addr2name_cache[saddr->sin_addr.s_addr] = std::pair<char *, time_t>(name, 0);
346  } else {
347  free(name);
348  }
349  }
350  free(addr);
351  addr2name_cache.unlock();
352 }
353 
354 
355 void
356 NetworkNameResolver::name_resolution_failed(char *name)
357 {
358  free(name);
359 }
360 
361 
362 void
363 NetworkNameResolver::address_resolution_failed(struct sockaddr *addr,
364  socklen_t addrlen)
365 {
366  free(addr);
367 }
368 
369 
370 /** Get long hostname.
371  * @return host name
372  */
373 const char *
374 NetworkNameResolver::hostname()
375 {
376  return __host_info->name();
377 }
378 
379 
380 /** Get short hostname.
381  * @return short hostname
382  */
383 const char *
384 NetworkNameResolver::short_hostname()
385 {
386  return __host_info->short_name();
387 }
388 
389 } // end namespace fawkes