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 
39 namespace 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  */
120 bool
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  */
173 bool
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  */
206 void
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  */
225 void
226 NetworkNameResolverThread::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  */
250 void
251 NetworkNameResolverThread::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  */
266 void
267 NetworkNameResolverThread::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  */
279 void
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  */
293 void
294 NetworkNameResolverThread::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  */
302 void
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