resolver_thread.cpp

00001 
00002 /***************************************************************************
00003  *  resolver_thread.cpp - Fawkes network name resolver thread
00004  *
00005  *  Created: Fri May 11 22:12:51 2007
00006  *  Copyright  2006-2007  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <netcomm/utils/resolver_thread.h>
00025 #include <netcomm/utils/resolver.h>
00026 #ifdef HAVE_AVAHI
00027 #include <netcomm/dns-sd/avahi_thread.h>
00028 #endif
00029 #include <core/exceptions/system.h>
00030 
00031 #include <sys/types.h>
00032 #include <netdb.h>
00033 #include <netinet/in.h>
00034 #include <cstring>
00035 #include <cstdlib>
00036 
00037 namespace fawkes {
00038 
00039 /** @class NetworkNameResolverThread <netcomm/utils/resolver_thread.h>
00040  * Worker thread for NetworkNameResolver.
00041  * This thread does the work for the NetworkNameResolver. It runs concurrently
00042  * to the rest of the software and executes name and address lookups in a
00043  * non-blocking fashion.
00044  *
00045  * This class should not be used directly, but NetworkNameResolver should
00046  * be used instead.
00047  *
00048  * @see NetworkNameResolver
00049  * @ingroup NetComm
00050  * @author Tim Niemueller
00051  */
00052 
00053 
00054 /** Constructor.
00055  * Available only if Avahi is available at compile time.
00056  * @param resolver network name resolver to call for results
00057  * @param avahi_thread Avahi thread, may be NULL in which case mDNS via
00058  * Avahi is not used.
00059  */
00060 NetworkNameResolverThread::NetworkNameResolverThread(NetworkNameResolver *resolver,
00061                                                      AvahiThread *avahi_thread)
00062   : Thread("NetworkNameResolverThread", Thread::OPMODE_WAITFORWAKEUP)
00063 {
00064   __resolver = resolver;
00065   __addrq_mutex   = new Mutex();
00066   __namesq_mutex  = new Mutex();
00067 
00068   __namesq_active = 0;
00069   __namesq        = &__namesqs[0];
00070   __namesq_proc   = &__namesqs[1];
00071 
00072   __addrq_active  = 0;
00073   __addrq         = &__addrqs[0];
00074   __addrq_proc    = &__addrqs[1];
00075 
00076 #ifdef HAVE_AVAHI
00077   __avahi_thread = avahi_thread;
00078 #endif
00079 }
00080 
00081 /** Destructor. */
00082 NetworkNameResolverThread::~NetworkNameResolverThread()
00083 {
00084   __namesq_mutex->lock();
00085   while ( ! __namesq->empty() ) {
00086     NamesQMap::iterator nqit = __namesq->begin();
00087     char *nqn = (*nqit);
00088     __namesq->erase(nqit);
00089     free(nqn);
00090   }
00091   while ( ! __namesq_proc->empty() ) {
00092     NamesQMap::iterator nqit = __namesq_proc->begin();
00093     char *nqn = (*nqit);
00094     __namesq->erase(nqit);
00095     free(nqn);
00096   }
00097   __namesq_mutex->unlock();
00098   __addrq_mutex->lock();
00099   while ( ! __addrq->empty() ) {
00100     AddrQMap::iterator nqit = __addrq->begin();
00101     free(nqit->second.first);
00102     __addrq->erase(nqit);
00103   }
00104   // The next operation cannot be locked, but we make the (valid) assumption
00105   // that the thread is not running when it is destructed, this situation is
00106   // an error anyway
00107   while ( ! __addrq_proc->empty() ) {
00108     AddrQMap::iterator nqit = __addrq_proc->begin();
00109     free(nqit->second.first);
00110     __addrq->erase(nqit);
00111   }
00112   __addrq_mutex->unlock();
00113   delete __addrq_mutex;
00114   delete __namesq_mutex;
00115 }
00116 
00117 
00118 /** Immediately resolve a name.
00119  * This tries to lookup a name with the getaddrinfo() and if the name ends with
00120  * .local (the host is in the .local domain) and an Avahi thread has been supplied
00121  * Avahi is used to lookup the hostname as well, but this does not happen immediately
00122  * because this can take some time.
00123  * @param name host name to lookup
00124  * @param addr upon return and success the address result will be stored here in a
00125  * newly allocated buffer which you have to free after use using free().
00126  * @param addr_len upon return and success contains the length of addr in bytes
00127  * @return true if the name has been successfully resolved in which case addr and
00128  * addr_len carry the result, false otherwise
00129  */
00130 bool
00131 NetworkNameResolverThread::resolve_name_immediately(const char *name,
00132                                                     struct sockaddr **addr, socklen_t *addr_len)
00133 {
00134   bool found = false;
00135 
00136   // First try a regular lookup
00137   struct addrinfo *ai;
00138   if ( getaddrinfo(name, NULL, NULL, &ai) == 0 ) {
00139     // return the first result
00140     struct sockaddr *tmp = (struct sockaddr *)malloc(ai->ai_addrlen);
00141     memcpy(tmp, ai->ai_addr, ai->ai_addrlen);
00142     *addr = tmp;
00143     *addr_len = ai->ai_addrlen;
00144     freeaddrinfo(ai);
00145     found = true;
00146   }
00147 
00148 #ifdef HAVE_AVAHI
00149   // resolve names in .local domain with Avahi if available
00150   char *n = (char *)name + strlen(name) - 6; // 6 == strlen(".local")
00151   const char *f = strstr(name, ".local");
00152   if ( __avahi_thread && f && (f == n) ) {
00153     __avahi_thread->resolve_name(name, this);          
00154   /*
00155   } else {
00156     printf("NOT ordering avahi_thread lookup\n");
00157     if ( ! avahi_thread ) 
00158       printf("No avahi resolver\n");
00159     if ( ! f ) {
00160       printf(".local not found\n");
00161     }
00162     if ( f != n ) {
00163       printf(".local at wrong location\n");
00164     }
00165   */
00166   }
00167 #endif
00168 
00169   return found;
00170 }
00171 
00172 
00173 /** Immediately resolve address.
00174  * This tries to lookup the address with the getnameinfo(). If that fails a textual
00175  * representation of the address is created. Additionally if an Avahi thread has
00176  * @param addr pointer to a struct of type struct sockaddr_in with the address to
00177  * lookup
00178  * @param addr_len length of addr in bytes
00179  * @param name contains a newly allocated buffer upon successful return that you have
00180  * to free after use using free().
00181  * @param namefound true, if the name could be resolved, false if it was just transformed
00182  * to a textual representation
00183  * @return true if the address has been successfully resolved in which case name
00184  * carries the result, false otherwise
00185  */
00186 bool
00187 NetworkNameResolverThread::resolve_address_immediately(struct sockaddr *addr, socklen_t addr_len,
00188                                                        char **name, bool *namefound)
00189 {
00190   bool found = false;
00191   char hbuf[NI_MAXHOST];
00192   if ( getnameinfo(addr, addr_len, hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD) == 0 ) {
00193     char *tmp = (char *)malloc(strlen(hbuf) + 1);
00194     if ( ! tmp ) {
00195       throw OutOfMemoryException();
00196     }
00197     strcpy(tmp, hbuf);
00198     *name = tmp;
00199     *namefound = true;
00200     found = true;
00201   } else if ( getnameinfo(addr, addr_len, hbuf, sizeof(hbuf), NULL, 0, 0) == 0 ) {
00202     char *tmp = (char *)malloc(strlen(hbuf) + 1);
00203     if ( ! tmp ) {
00204       throw OutOfMemoryException();
00205     }
00206     strcpy(tmp, hbuf);
00207     *name = tmp;
00208     *namefound = false;
00209     found = true;
00210   }
00211 
00212 #ifdef HAVE_AVAHI
00213   if ( __avahi_thread ) {
00214     __avahi_thread->resolve_address(addr, addr_len, this);
00215   }
00216 #endif
00217 
00218   return found;
00219 }
00220 
00221 
00222 /** Enqueue name for resolution.
00223  * The name is enqueued and the resolver thread woken up. The result is reported
00224  * to the resolver given to the constructor.
00225  * @param name name to resolve
00226  */
00227 void
00228 NetworkNameResolverThread::resolve_name(const char *name)
00229 {
00230   __namesq_mutex->lock();
00231   if ( __namesq->find((char *)name) == __namesq->end() ) {
00232     char *tmp = strdup(name);
00233     __namesq->insert(tmp);
00234     __namesq_mutex->unlock();
00235     wakeup();
00236   } else {
00237     __namesq_mutex->unlock();
00238   }
00239 }
00240 
00241 
00242 /** Enqueue address for resolution.
00243  * The address is enqueued and the resolver thread woken up. The result is reported
00244  * to the resolver given to the constructor.
00245  * @param addr address to resolve, must be a struct sockaddr_in
00246  * @param addrlen length of addr
00247  */
00248 void
00249 NetworkNameResolverThread::resolve_address(struct sockaddr *addr, socklen_t addrlen)
00250 {
00251   struct ::sockaddr_in *saddr = (struct ::sockaddr_in *)addr;
00252   __addrq_mutex->lock();
00253   if ( __addrq->find(saddr->sin_addr.s_addr) == __addrq->end() ) {
00254     struct sockaddr *taddr = (struct sockaddr *)malloc(addrlen);
00255     memcpy(taddr, addr, addrlen);
00256     (*__addrq)[saddr->sin_addr.s_addr] = std::make_pair(taddr, addrlen);
00257     __addrq_mutex->unlock();
00258     wakeup();
00259   } else {
00260     __addrq_mutex->unlock();
00261   }
00262 }
00263 
00264 
00265 /** Name has been successfully resolved.
00266  * The ordered name lookup was successful for the given name resulting in
00267  * the given addr of addrlen bytes length.
00268  * Note that all of the parameters are given to the handler's ownership, that means
00269  * especially that the handler is responsible for freeing the associated memory
00270  * after it is done with the result using free() on name and addr.
00271  * @param name name that was resolved
00272  * @param addr resulting addr record, currently always of type struct sockaddr_in (only IPv4)
00273  * @param addrlen length of addr in bytes
00274  */
00275 void
00276 NetworkNameResolverThread::resolved_name(char *name,
00277                                          struct sockaddr *addr, socklen_t addrlen)
00278 {
00279   __resolver->name_resolved(name, addr, addrlen);
00280 }
00281 
00282 
00283 /** Address has been successfully resolved.
00284  * The ordered name lookup was successful for the given address resulting in
00285  * the given name.
00286  * Note that all of the parameters are given to the handler's ownership, that means
00287  * especially that the handler is responsible for freeing the associated memory
00288  * after it is done with the result using free() on name and addr.
00289  * @param name the resulting hostname
00290  * @param addr addr record, currently always of type struct sockaddr_in (only IPv4)
00291  * @param addrlen length of addr in bytes
00292  */
00293 void
00294 NetworkNameResolverThread::resolved_address(struct sockaddr_in *addr, socklen_t addrlen,
00295                                             char *name)
00296 {
00297   __resolver->addr_resolved((struct sockaddr *)addr, addrlen, name, true);
00298 }
00299 
00300 
00301 /** Name resolution failed.
00302  * The given hostname could not be resolved.
00303  * Note that the parameter name is given to the handler's ownership. This means
00304  * especially that the handler is responsible for freeing the memory with free()
00305  * after it is done with the variable.
00306  * @param name name whose lookup failed
00307  */
00308 void
00309 NetworkNameResolverThread::name_resolution_failed(char *name)
00310 {
00311   __resolver->name_resolution_failed(name);
00312 }
00313 
00314 
00315 /** Address resolution failed.
00316  * The given address could not be resolved.
00317  * Note that the parameter addr is given to the handler's ownership. This means
00318  * especially that the handler is responsible for freeing the memory with free()
00319  * after it is done with the variable.
00320  * @param addr address whose lookup failed
00321  * @param addrlen length of address
00322  */
00323 void
00324 NetworkNameResolverThread::address_resolution_failed(struct sockaddr_in *addr, socklen_t addrlen)
00325 {
00326   __resolver->address_resolution_failed((struct sockaddr *)addr, addrlen);
00327 }
00328 
00329 
00330 /** Thread loop.
00331  * This will carry out all enqueued resolution operations.
00332  */
00333 void
00334 NetworkNameResolverThread::loop()
00335 {
00336   __addrq_mutex->lock();
00337   __addrq_proc = __addrq;
00338   __addrq_active = 1 - __addrq_active;
00339   __addrq = &__addrqs[__addrq_active];
00340   __addrq_mutex->unlock();
00341   AddrQMap::iterator aqit;
00342   while ( ! __addrq_proc->empty() ) {
00343     aqit = __addrq_proc->begin();
00344     
00345     char *name;
00346     bool  namefound;
00347 
00348     if ( resolve_address_immediately(aqit->second.first, aqit->second.second, &name, &namefound) ) {
00349       __resolver->addr_resolved(aqit->second.first, aqit->second.second, name, namefound);
00350     } else {
00351       __resolver->address_resolution_failed(aqit->second.first, aqit->second.second);
00352     }
00353     __addrq_proc->erase(aqit);
00354   }
00355 
00356   __namesq_mutex->lock();
00357   __namesq_proc = __namesq;
00358   __namesq_active = 1 - __namesq_active;
00359   __namesq = &__namesqs[__namesq_active];
00360   __namesq_mutex->unlock();
00361   NamesQMap::iterator nqit;
00362   while ( ! __namesq_proc->empty() ) {
00363     nqit = __namesq_proc->begin();
00364     char *nqn = (*nqit);
00365 
00366     struct sockaddr *addr;
00367     socklen_t addrlen;
00368 
00369     // we strdup here because otherwise we could not ensure
00370     // that the erase operation below can still suceed!
00371     // And even if we make it mandatory that the name_resolved will not
00372     // free the memory we would have the problem that it would be
00373     // unknown when the resolver may free the variable
00374     if ( resolve_name_immediately(nqn, &addr, &addrlen) ) {
00375       __resolver->name_resolved(strdup(nqn), addr, addrlen);
00376     } else {
00377       __resolver->name_resolution_failed(strdup(nqn));
00378     }
00379     __namesq_proc->erase(nqit);
00380     free(nqn);
00381   }
00382 }
00383 
00384 } // end namespace fawkes

Generated on Tue Feb 22 13:32:28 2011 for Fawkes API by  doxygen 1.4.7