Fawkes API  Fawkes Development Version
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  */
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. */
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
122 {
123  __cache_timeout = sec;
124 }
125 
126 
127 /** Get cache timeout.
128  * @return resolution cache timeout in seconds
129  */
130 unsigned int
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
142 {
143  addr2name_cache.lock();
144  addr2name_cache.clear();
145  addr2name_cache.unlock();
146  name2addr_cache.lock();
147  while ( ! name2addr_cache.empty() ) {
148  n2acit = name2addr_cache.begin();
149  free(n2acit->second.first);
150  name2addr_cache.erase(n2acit);
151  }
152  name2addr_cache.unlock();
153  __host_info->update();
154 
155  /* Leads to a segfault, if one element is in the queue it is deleted
156  * two times, do not use
157  for (n2acit = name2addr_cache.begin(); n2acit != name2addr_cache.end(); ++n2acit) {
158  free((*n2acit).first);
159  free((*n2acit).second.first);
160  }
161  */
162 }
163 
164 
165 /** Resolve name.
166  * This will lookup a name from the cache and return the value if available.
167  * If there is no entry in the cache this will order a concurrent lookup of the
168  * name an return a failure.
169  * @param name name to resolve
170  * @param addr contains a pointer to the address record upon return, this record
171  * is in the cache, so you may not free the resulting address! The address is
172  * always of type struct sockaddr_in (IPv4) at the moment.
173  * @param addrlen contains the length of addr in bytes upon return
174  * @return true if resolution was successful, false otherwise
175  */
176 bool
178  struct sockaddr **addr, socklen_t *addrlen)
179 {
180  name2addr_cache.lock();
181 
182  if ( name2addr_cache.find( (char *)name ) != name2addr_cache.end() ) {
183  // the name is in the cache, refetch?
184  std::pair<struct sockaddr *, time_t> &nrec = name2addr_cache[(char *)name];
185  if ( nrec.second <= time(NULL) ) {
186  // entry outdated, retry
187  resolver_thread->resolve_name(name);
188  }
189  *addr = nrec.first;
190  *addrlen = sizeof(struct sockaddr_in);
191  name2addr_cache.unlock();
192  return true;
193  } else {
194  name2addr_cache.unlock();
195  resolver_thread->resolve_name(name);
196  return false;
197  }
198 }
199 
200 
201 /** Resolve name and wait for the result.
202  * This will lookup a name from the cache and return the value if available.
203  * If there is no entry in the cache this will order a concurrent lookup of the
204  * name and wait for the result.
205  * @param name name to resolve
206  * @param addr contains a pointer to the address record upon return, this record
207  * is in the cache, so you may not free the resulting address! The address is
208  * always of type struct sockaddr_in (IPv4) at the moment.
209  * @param addrlen contains the length of addr in bytes upon return
210  * @return true if resolution was successful, false otherwise
211  */
212 bool
214  struct sockaddr **addr, socklen_t *addrlen)
215 {
216  if ( resolve_name(name, addr, addrlen) ) {
217  return true;
218  } else {
219  struct sockaddr *_addr;
220  socklen_t _addrlen;
221  if ( resolver_thread->resolve_name_immediately(name, &_addr, &_addrlen) ) {
222  name_resolved(strdup(name), _addr, _addrlen);
223  *addr = _addr;
224  *addrlen = _addrlen;
225  return true;
226  } else {
227  return false;
228  }
229  }
230 }
231 
232 
233 /** Resolve address.
234  * This will lookup an address from the cache and return the value if available.
235  * If there is no entry in the cache this will order a concurrent lookup of the
236  * address and return the textual representation of the address.
237  * @param addr address to resolve
238  * @param addr_len length of addr in bytes
239  * @param name contains a pointer to the name upon return. Note that this record
240  * resides in the cache and may not be freed.
241  * @return true if resolution was successful, false otherwise
242  */
243 bool
244 NetworkNameResolver::resolve_address(struct sockaddr *addr, socklen_t addr_len, std::string &name)
245 {
246  addr2name_cache.lock();
247  struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
248 
249  if ( addr2name_cache.find( saddr->sin_addr.s_addr ) != addr2name_cache.end() ) {
250  // the name is in the cache, refetch?
251  std::pair<std::string, time_t> &nrec = addr2name_cache[saddr->sin_addr.s_addr];
252  name = nrec.first;
253  if ( nrec.second <= time(NULL) ) {
254  // entry outdated, retry
255  addr2name_cache.unlock();
256  resolver_thread->resolve_address(addr, addr_len);
257  } else {
258  addr2name_cache.unlock();
259  }
260  } else {
261  char tmp[INET_ADDRSTRLEN];
262  if ( inet_ntop(AF_INET, &(saddr->sin_addr), tmp, sizeof(tmp)) ) {
263  char *n = strdup(tmp);
264 
265  addr2name_cache[saddr->sin_addr.s_addr] = std::pair<char *, time_t>(n, time(NULL) + __cache_timeout);
266  name = n;
267  addr2name_cache.unlock();
268  } else {
269  addr2name_cache.unlock();
270  return false;
271  }
272 
273  resolver_thread->resolve_address(addr, addr_len);
274  }
275 
276  return true;
277 
278  /*
279  char hbuf[NI_MAXHOST];
280  if ( getnameinfo(addr, addr_len, hbuf, sizeof(hbuf), NULL, 0, 0) == -1 ) {
281  return false;
282  } else {
283  char *tmp = (char *)malloc(strlen(hbuf) + 1);
284  if ( ! tmp ) {
285  throw OutOfMemoryException();
286  }
287  strcpy(tmp, hbuf);
288  *name = tmp;
289  return true;
290  }
291  */
292 }
293 
294 
295 /** Name has been resolved by resolver thread.
296  * This is an internal function, if you modify it, please make absolutely sure that you
297  * understand the caches, especially when the key has to be freed! Also note that we
298  * take over the ownership name and addr and are responsible for freeing at some
299  * point!
300  * @param name host name
301  * @param addr address structure
302  * @param addrlen length in bytes of addr
303  */
304 void
305 NetworkNameResolver::name_resolved(std::string name, struct sockaddr *addr,
306  socklen_t addrlen)
307 {
308  name2addr_cache.lock();
309  if ( (n2acit = name2addr_cache.find(name)) != name2addr_cache.end() ) {
310  // delete old entry
311  free(n2acit->second.first);
312  name2addr_cache.erase(n2acit);
313  }
314  name2addr_cache[name] = std::pair<struct sockaddr *, time_t>(addr, time(NULL) + __cache_timeout);
315  name2addr_cache.unlock();
316 }
317 
318 
319 void
320 NetworkNameResolver::addr_resolved(struct sockaddr *addr,
321  socklen_t addrlen,
322  std::string name, bool namefound)
323 {
324  addr2name_cache.lock();
325  struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
326  if (namefound) {
327  if ((a2ncit = addr2name_cache.find( saddr->sin_addr.s_addr )) != addr2name_cache.end() ) {
328  // delete old entry
329  addr2name_cache.erase(a2ncit);
330  addr2name_cache[saddr->sin_addr.s_addr] =
331  std::make_pair(name, time(NULL) + __cache_timeout);
332  }
333  } else {
334  if ((a2ncit = addr2name_cache.find( saddr->sin_addr.s_addr )) == addr2name_cache.end() ) {
335  addr2name_cache[saddr->sin_addr.s_addr] = std::make_pair(name, 0);
336  }
337  }
338  free(addr);
339  addr2name_cache.unlock();
340 }
341 
342 
343 void
344 NetworkNameResolver::name_resolution_failed(std::string name)
345 {
346 }
347 
348 
349 void
350 NetworkNameResolver::address_resolution_failed(struct sockaddr *addr,
351  socklen_t addrlen)
352 {
353  free(addr);
354 }
355 
356 
357 /** Get long hostname.
358  * @return host name
359  */
360 const char *
362 {
363  return __host_info->name();
364 }
365 
366 
367 /** Get short hostname.
368  * @return short hostname
369  */
370 const char *
372 {
373  return __host_info->short_name();
374 }
375 
376 } // end namespace fawkes
bool resolve_name(const char *name, struct sockaddr **addr, socklen_t *addrlen)
Resolve name.
Definition: resolver.cpp:177
const char * hostname()
Get long hostname.
Definition: resolver.cpp:361
const char * short_name()
Get short hostname (up to first dot).
Definition: hostinfo.cpp:114
void resolve_address(struct sockaddr *addr, socklen_t addrlen)
Enqueue address for resolution.
void update()
Update information.
Definition: hostinfo.cpp:73
Fawkes library namespace.
Worker thread for NetworkNameResolver.
~NetworkNameResolver()
Destructor.
Definition: resolver.cpp:105
void flush_cache()
Flush cache.
Definition: resolver.cpp:141
const char * short_hostname()
Get short hostname.
Definition: resolver.cpp:371
Host information.
Definition: hostinfo.h:31
void resolve_name(const std::string &name)
Enqueue name for resolution.
const char * name()
Get full hostname.
Definition: hostinfo.cpp:104
bool resolve_address(struct sockaddr *addr, socklen_t addr_len, std::string &name)
Resolve address.
Definition: resolver.cpp:244
Avahi main thread.
Definition: avahi_thread.h:54
void cancel()
Cancel a thread.
Definition: thread.cpp:651
void unlock() const
Unlock map.
Definition: lock_hashmap.h:143
unsigned int cache_timeout()
Get cache timeout.
Definition: resolver.cpp:131
void join()
Join the thread.
Definition: thread.cpp:610
bool resolve_name_blocking(const char *name, struct sockaddr **addr, socklen_t *addrlen)
Resolve name and wait for the result.
Definition: resolver.cpp:213
void set_cache_timeout(unsigned int sec)
Set cache timeout.
Definition: resolver.cpp:121
NetworkNameResolver(AvahiThread *avahi_thread=NULL)
Constructor.
Definition: resolver.cpp:89
void lock() const
Lock map.
Definition: lock_hashmap.h:123
bool resolve_name_immediately(const std::string &name, struct sockaddr **addr, socklen_t *addr_len)
Immediately resolve a name.
void start(bool wait=true)
Call this method to start the thread.
Definition: thread.cpp:511