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