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