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