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
39namespace 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 */
115void
117{
118 cache_timeout_ = sec;
119}
120
121/** Get cache timeout.
122 * @return resolution cache timeout in seconds
123 */
124unsigned 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 */
133void
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 */
168bool
169NetworkNameResolver::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 */
202bool
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 */
233bool
234NetworkNameResolver::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 */
294void
295NetworkNameResolver::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
307void
308NetworkNameResolver::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
330void
331NetworkNameResolver::name_resolution_failed(std::string name)
332{
333}
334
335void
336NetworkNameResolver::address_resolution_failed(struct sockaddr *addr, socklen_t addrlen)
337{
338 free(addr);
339}
340
341/** Get long hostname.
342 * @return host name
343 */
344const char *
346{
347 return host_info_->name();
348}
349
350/** Get short hostname.
351 * @return short hostname
352 */
353const char *
355{
356 return host_info_->short_name();
357}
358
359} // end namespace fawkes
Avahi main thread.
Definition: avahi_thread.h:55
Host information.
Definition: hostinfo.h:32
const char * name()
Get full hostname.
Definition: hostinfo.cpp:100
void update()
Update information.
Definition: hostinfo.cpp:70
const char * short_name()
Get short hostname (up to first dot).
Definition: hostinfo.cpp:109
void lock() const
Lock map.
Definition: lock_hashmap.h:118
void unlock() const
Unlock map.
Definition: lock_hashmap.h:136
Worker thread for NetworkNameResolver.
bool resolve_name_immediately(const std::string &name, struct sockaddr **addr, socklen_t *addr_len)
Immediately resolve a name.
void resolve_name(const std::string &name)
Enqueue name for resolution.
void resolve_address(struct sockaddr *addr, socklen_t addrlen)
Enqueue address for resolution.
unsigned int cache_timeout()
Get cache timeout.
Definition: resolver.cpp:125
void set_cache_timeout(unsigned int sec)
Set cache timeout.
Definition: resolver.cpp:116
void flush_cache()
Flush cache.
Definition: resolver.cpp:134
bool resolve_address(struct sockaddr *addr, socklen_t addr_len, std::string &name)
Resolve address.
Definition: resolver.cpp:234
NetworkNameResolver(AvahiThread *avahi_thread=NULL)
Constructor.
Definition: resolver.cpp:85
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
~NetworkNameResolver()
Destructor.
Definition: resolver.cpp:100
bool resolve_name_blocking(const char *name, struct sockaddr **addr, socklen_t *addrlen)
Resolve name and wait for the result.
Definition: resolver.cpp:203
const char * short_hostname()
Get short hostname.
Definition: resolver.cpp:354
void start(bool wait=true)
Call this method to start the thread.
Definition: thread.cpp:499
void join()
Join the thread.
Definition: thread.cpp:597
void cancel()
Cancel a thread.
Definition: thread.cpp:646
Fawkes library namespace.