Fawkes API Fawkes Development Version
resolver_thread.cpp
1
2/***************************************************************************
3 * resolver_thread.cpp - Fawkes network name resolver thread
4 *
5 * Created: Fri May 11 22:12:51 2007
6 * Copyright 2006-2007 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/addr_size.h>
25#include <netcomm/utils/resolver.h>
26#include <netcomm/utils/resolver_thread.h>
27#ifdef HAVE_AVAHI
28# include <netcomm/dns-sd/avahi_thread.h>
29#endif
30#include <core/exceptions/system.h>
31#include <netinet/in.h>
32#include <sys/types.h>
33
34#include <algorithm>
35#include <cstdlib>
36#include <cstring>
37#include <netdb.h>
38
39namespace fawkes {
40
41/** @class NetworkNameResolverThread <netcomm/utils/resolver_thread.h>
42 * Worker thread for NetworkNameResolver.
43 * This thread does the work for the NetworkNameResolver. It runs concurrently
44 * to the rest of the software and executes name and address lookups in a
45 * non-blocking fashion.
46 *
47 * This class should not be used directly, but NetworkNameResolver should
48 * be used instead.
49 *
50 * @see NetworkNameResolver
51 * @ingroup NetComm
52 * @author Tim Niemueller
53 */
54
55/** Constructor.
56 * Available only if Avahi is available at compile time.
57 * @param resolver network name resolver to call for results
58 * @param avahi_thread Avahi thread, may be NULL in which case mDNS via
59 * Avahi is not used.
60 */
62 AvahiThread * avahi_thread)
63: Thread("NetworkNameResolverThread", Thread::OPMODE_WAITFORWAKEUP)
64{
65 resolver_ = resolver;
66 addrq_mutex_ = new Mutex();
67 namesq_mutex_ = new Mutex();
68
69 namesq_active_ = 0;
70 namesq_ = &namesqs_[0];
71 namesq_proc_ = &namesqs_[1];
72
73 addrq_active_ = 0;
74 addrq_ = &addrqs_[0];
75 addrq_proc_ = &addrqs_[1];
76
77#ifdef HAVE_AVAHI
78 avahi_thread_ = avahi_thread;
79#endif
80}
81
82/** Destructor. */
84{
85 namesq_mutex_->lock();
86 namesq_->clear();
87 namesq_proc_->clear();
88 namesq_mutex_->unlock();
89 addrq_mutex_->lock();
90 while (!addrq_->empty()) {
91 AddrQList::iterator nqit = addrq_->begin();
92 free(*nqit);
93 addrq_->erase(nqit);
94 }
95 // The next operation cannot be locked, but we make the (valid) assumption
96 // that the thread is not running when it is destructed, this situation is
97 // an error anyway
98 while (!addrq_proc_->empty()) {
99 AddrQList::iterator nqit = addrq_proc_->begin();
100 free(*nqit);
101 addrq_->erase(nqit);
102 }
103 addrq_mutex_->unlock();
104 delete addrq_mutex_;
105 delete namesq_mutex_;
106}
107
108/** Immediately resolve a name.
109 * This tries to lookup a name with the getaddrinfo() and if the name ends with
110 * .local (the host is in the .local domain) and an Avahi thread has been supplied
111 * Avahi is used to lookup the hostname as well, but this does not happen immediately
112 * because this can take some time.
113 * @param name host name to lookup
114 * @param addr upon return and success the address result will be stored here in a
115 * newly allocated buffer which you have to free after use using free().
116 * @param addr_len upon return and success contains the length of addr in bytes
117 * @return true if the name has been successfully resolved in which case addr and
118 * addr_len carry the result, false otherwise
119 */
120bool
122 struct sockaddr ** addr,
123 socklen_t * addr_len)
124{
125 bool found = false;
126
127 // First try a regular lookup
128 struct addrinfo *ai;
129 if (getaddrinfo(name.c_str(), NULL, NULL, &ai) == 0) {
130 // return the first result
131 struct sockaddr *tmp = (struct sockaddr *)malloc(ai->ai_addrlen);
132 memcpy(tmp, ai->ai_addr, ai->ai_addrlen);
133 *addr = tmp;
134 *addr_len = ai->ai_addrlen;
135 freeaddrinfo(ai);
136 found = true;
137 }
138
139#ifdef HAVE_AVAHI
140 // resolve names in .local domain with Avahi if available
141 if (avahi_thread_ && name.find(".local") == name.length() - 6) { // 6 == strlen(".local")
142 avahi_thread_->resolve_name(name.c_str(), this);
143 /*
144 } else {
145 printf("NOT ordering avahi_thread lookup\n");
146 if ( ! avahi_thread )
147 printf("No avahi resolver\n");
148 if ( ! f ) {
149 printf(".local not found\n");
150 }
151 if ( f != n ) {
152 printf(".local at wrong location\n");
153 }
154 */
155 }
156#endif
157
158 return found;
159}
160
161/** Immediately resolve address.
162 * This tries to lookup the address with the getnameinfo(). If that fails a textual
163 * representation of the address is created. Additionally if an Avahi thread has
164 * @param addr pointer to a struct of type struct sockaddr with the address to
165 * lookup
166 * @param name contains a newly allocated buffer upon successful return that you have
167 * to free after use using free().
168 * @param namefound true, if the name could be resolved, false if it was just transformed
169 * to a textual representation
170 * @return true if the address has been successfully resolved in which case name
171 * carries the result, false otherwise
172 */
173bool
175 std::string & name,
176 bool & namefound)
177{
178 bool found = false;
179 char hbuf[NI_MAXHOST];
180 socklen_t addr_len = addr->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
181
182 if (getnameinfo(addr, addr_len, hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD) == 0) {
183 name = hbuf;
184 namefound = true;
185 found = true;
186 } else if (getnameinfo(addr, addr_len, hbuf, sizeof(hbuf), NULL, 0, 0) == 0) {
187 name = hbuf;
188 namefound = false;
189 found = true;
190 }
191
192#ifdef HAVE_AVAHI
193 if (avahi_thread_) {
194 avahi_thread_->resolve_address(addr, addr_len, this);
195 }
196#endif
197
198 return found;
199}
200
201/** Enqueue name for resolution.
202 * The name is enqueued and the resolver thread woken up. The result is reported
203 * to the resolver given to the constructor.
204 * @param name name to resolve
205 */
206void
208{
209 namesq_mutex_->lock();
210 if (namesq_->find(name) == namesq_->end()) {
211 namesq_->insert(name);
212 namesq_mutex_->unlock();
213 wakeup();
214 } else {
215 namesq_mutex_->unlock();
216 }
217}
218
219/** Enqueue address for resolution.
220 * The address is enqueued and the resolver thread woken up. The result is reported
221 * to the resolver given to the constructor.
222 * @param addr address to resolve, must be a struct sockaddr
223 * @param addrlen length of addr
224 */
225void
226NetworkNameResolverThread::resolve_address(struct sockaddr *addr, socklen_t addrlen)
227{
228 addrq_mutex_->lock();
229 if (std::find(addrq_->begin(), addrq_->end(), addr) == addrq_->end()) {
230 struct sockaddr *taddr = (struct sockaddr *)malloc(addrlen);
231 memcpy(taddr, addr, addrlen);
232 addrq_->push_back(taddr);
233 addrq_mutex_->unlock();
234 wakeup();
235 } else {
236 addrq_mutex_->unlock();
237 }
238}
239
240/** Name has been successfully resolved.
241 * The ordered name lookup was successful for the given name resulting in
242 * the given addr of addrlen bytes length.
243 * Note that all of the parameters are given to the handler's ownership, that means
244 * especially that the handler is responsible for freeing the associated memory
245 * after it is done with the result using free() on name and addr.
246 * @param name name that was resolved
247 * @param addr resulting addr record, currently always of type struct sockaddr_in (only IPv4)
248 * @param addrlen length of addr in bytes
249 */
250void
251NetworkNameResolverThread::resolved_name(char *name, struct sockaddr *addr, socklen_t addrlen)
252{
253 resolver_->name_resolved(name, addr, addrlen);
254}
255
256/** Address has been successfully resolved.
257 * The ordered name lookup was successful for the given address resulting in
258 * the given name.
259 * Note that all of the parameters are given to the handler's ownership, that means
260 * especially that the handler is responsible for freeing the associated memory
261 * after it is done with the result using free() on name and addr.
262 * @param name the resulting hostname
263 * @param addr addr record, currently always of type struct sockaddr_in (only IPv4)
264 * @param addrlen length of addr in bytes
265 */
266void
267NetworkNameResolverThread::resolved_address(struct sockaddr *addr, socklen_t addrlen, char *name)
268{
269 resolver_->addr_resolved(addr, addrlen, name, true);
270}
271
272/** Name resolution failed.
273 * The given hostname could not be resolved.
274 * Note that the parameter name is given to the handler's ownership. This means
275 * especially that the handler is responsible for freeing the memory with free()
276 * after it is done with the variable.
277 * @param name name whose lookup failed
278 */
279void
281{
282 resolver_->name_resolution_failed(name);
283}
284
285/** Address resolution failed.
286 * The given address could not be resolved.
287 * Note that the parameter addr is given to the handler's ownership. This means
288 * especially that the handler is responsible for freeing the memory with free()
289 * after it is done with the variable.
290 * @param addr address whose lookup failed
291 * @param addrlen length of address
292 */
293void
294NetworkNameResolverThread::address_resolution_failed(struct sockaddr *addr, socklen_t addrlen)
295{
296 resolver_->address_resolution_failed(addr, addrlen);
297}
298
299/** Thread loop.
300 * This will carry out all enqueued resolution operations.
301 */
302void
304{
305 addrq_mutex_->lock();
306 addrq_proc_ = addrq_;
307 addrq_active_ = 1 - addrq_active_;
308 addrq_ = &addrqs_[addrq_active_];
309 addrq_mutex_->unlock();
310 AddrQList::iterator aqit;
311 while (!addrq_proc_->empty()) {
312 aqit = addrq_proc_->begin();
313
314 std::string name;
315 bool namefound;
316
317 if (resolve_address_immediately(*aqit, name, namefound)) {
318 resolver_->addr_resolved(*aqit, sock_addr_size(*aqit), name, namefound);
319 } else {
320 resolver_->address_resolution_failed(*aqit, sock_addr_size(*aqit));
321 }
322 addrq_proc_->erase(aqit);
323 }
324
325 namesq_mutex_->lock();
326 namesq_proc_ = namesq_;
327 namesq_active_ = 1 - namesq_active_;
328 namesq_ = &namesqs_[namesq_active_];
329 namesq_mutex_->unlock();
330 NamesQMap::iterator nqit;
331 while (!namesq_proc_->empty()) {
332 nqit = namesq_proc_->begin();
333 struct sockaddr *addr;
334 socklen_t addrlen;
335
336 if (resolve_name_immediately(*nqit, &addr, &addrlen)) {
337 resolver_->name_resolved(*nqit, addr, addrlen);
338 } else {
339 resolver_->name_resolution_failed(*nqit);
340 }
341 namesq_proc_->erase(nqit);
342 }
343}
344
345} // end namespace fawkes
Avahi main thread.
Definition: avahi_thread.h:55
Mutex mutual exclusion lock.
Definition: mutex.h:33
void lock()
Lock this mutex.
Definition: mutex.cpp:87
void unlock()
Unlock the mutex.
Definition: mutex.cpp:131
virtual void address_resolution_failed(struct sockaddr *addr, socklen_t addrlen)
Address resolution failed.
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.
virtual void loop()
Thread loop.
virtual void name_resolution_failed(char *name)
Name resolution failed.
NetworkNameResolverThread(NetworkNameResolver *resolver, AvahiThread *avahi_thread=NULL)
Constructor.
virtual void resolved_name(char *name, struct sockaddr *addr, socklen_t addrlen)
Name has been successfully resolved.
bool resolve_address_immediately(struct sockaddr *addr, std::string &name, bool &namefound)
Immediately resolve address.
virtual void resolved_address(struct sockaddr *addr, socklen_t addrlen, char *name)
Address has been successfully resolved.
Network name and address resolver.
Definition: resolver.h:45
Thread class encapsulation of pthreads.
Definition: thread.h:46
const char * name() const
Get name of thread.
Definition: thread.h:100
void wakeup()
Wake up thread.
Definition: thread.cpp:995
Fawkes library namespace.
size_t sock_addr_size(const struct sockaddr *a)
Get canonical size of sockaddr structure.
Definition: addr_size.h:38