Fawkes API  Fawkes Development Version
resolver.cpp
00001 
00002 /***************************************************************************
00003  *  resolver.cpp - Fawkes network name resolver
00004  *
00005  *  Created: Tue Nov 14 14:25:52 2006
00006  *  Copyright  2006-2009  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.h>
00025 #include <netcomm/utils/resolver_thread.h>
00026 #include <core/exceptions/system.h>
00027 #include <core/threading/mutex_locker.h>
00028 #include <utils/system/hostinfo.h>
00029 
00030 #include <sys/types.h>
00031 #include <arpa/inet.h>
00032 #include <netdb.h>
00033 #include <netinet/in.h>
00034 #include <cstring>
00035 #include <cstdlib>
00036 #include <cstdio>
00037 #include <unistd.h>
00038 
00039 namespace fawkes {
00040 #if 0 /* just to make Emacs auto-indent happy */
00041 }
00042 #endif
00043 
00044 /** @class NetworkNameResolver <netcomm/utils/resolver.h>
00045  * Network name and address resolver.
00046  * This class implements a facility to resolve host names to addresses
00047  * and vice versa. It provides a simplified interface that only supports
00048  * IPv4 and will return only the first answer received. It has
00049  * optional support for using mDNS via Avahi for lookups on the local
00050  * network in the .local domain.
00051  *
00052  * Quite some effort has been done to ensure the speediness of this
00053  * implementation. It is assumed that a fast lookup is the most important
00054  * thing for the resolver. This means especially that under some circumstances
00055  * no result can be supplied on the first call but just on a subsequent call
00056  * (it is not defined if or when a result will be available). So it is a
00057  * good choice to always call the resolver and let it do the right thing
00058  * and not cache names and addresses in your own application. This also
00059  * makes the cache more efficient since multiple threads may use this
00060  * single resolver. The resolver itself holds a resolver thread that
00061  * will do lookups concurrently which will update the cache with
00062  * results thus that subsequent calls will provide the correct
00063  * information from the cache.
00064  *
00065  * The resolver uses an internal cache of name to address and addres to
00066  * name mapping. If a valid lookup has happened this mapping is assumed
00067  * to be authoritative and that it will not change. If you want to flush
00068  * the cache from time to time you may use flush_cache() to do so.
00069  *
00070  * In general resolve_name() and resolve_address() immediately return. If no
00071  * answer is in the cache for resolve_address() it will just provide the textual
00072  * representation of the IP address. This is different for resolve_name(). If
00073  * no answer is available if will order a concurrent lookup and return a
00074  * lookup failure. Subsequent calls may succeed if the cache was successfully
00075  * updated by the concurrent resolver thread. If you need the answer to be
00076  * able to proceed use resolve_name_blocking(). This will wait until an
00077  * answer is available via the host lookup facilities of the system or
00078  * optional via mDNS.
00079  *
00080  * @ingroup NetComm
00081  * @author Tim Niemueller
00082  */
00083 
00084 
00085 /** Constructor.
00086  * This constructor us available if Avahi is available at compile time.
00087  * @param avahi_thread Optional avahi thread, Avahi is not used if NULL
00088  */
00089 NetworkNameResolver::NetworkNameResolver(AvahiThread *avahi_thread)
00090 {
00091   addr2name_cache.clear();
00092   name2addr_cache.clear();
00093   __cache_timeout = 30;
00094 
00095   resolver_thread = new NetworkNameResolverThread(this, avahi_thread);
00096   resolver_thread->start();
00097   // Wait for thread to start
00098   usleep(0);
00099 
00100   __host_info = new HostInfo();
00101 }
00102 
00103 
00104 /** Destructor. */
00105 NetworkNameResolver::~NetworkNameResolver()
00106 {
00107   flush_cache();
00108   resolver_thread->cancel();
00109   resolver_thread->join();
00110   delete resolver_thread;
00111   delete __host_info;
00112 }
00113 
00114 /** Set cache timeout.
00115  * The apply only applies to consecutive lookups, existing entries will expire
00116  * with the old timeout.
00117  * @param sec the timeout in seconds determines after which time successful
00118  * resolutions are purged from the cache.
00119  */
00120 void
00121 NetworkNameResolver::set_cache_timeout(unsigned int sec)
00122 {
00123   __cache_timeout = sec;
00124 }
00125 
00126 
00127 /** Get cache timeout.
00128  * @return resolution cache timeout in seconds
00129  */
00130 unsigned int
00131 NetworkNameResolver::cache_timeout()
00132 {
00133   return __cache_timeout;
00134 }
00135 
00136 
00137 /** Flush cache.
00138  * Flushes the caches for name to address and address to name mappings.
00139  */
00140 void
00141 NetworkNameResolver::flush_cache()
00142 {
00143   addr2name_cache.lock();
00144   while ( ! addr2name_cache.empty() ) {
00145     a2ncit = addr2name_cache.begin();
00146     free((*a2ncit).second.first);
00147     addr2name_cache.erase(a2ncit);
00148   }
00149   addr2name_cache.unlock();
00150   name2addr_cache.lock();
00151   while ( ! name2addr_cache.empty() ) {
00152     n2acit = name2addr_cache.begin();
00153     char *name = (*n2acit).first;
00154     free((*n2acit).second.first);
00155     name2addr_cache.erase(n2acit);
00156     free(name);
00157   }
00158   name2addr_cache.unlock();
00159   __host_info->update();
00160 
00161   /* Leads to a segfault, if one element is in the queue it is deleted
00162    * two times, do not use
00163   for (n2acit = name2addr_cache.begin(); n2acit != name2addr_cache.end(); ++n2acit) {
00164     free((*n2acit).first);
00165     free((*n2acit).second.first);
00166   }
00167   */
00168 }
00169 
00170 
00171 /** Resolve name.
00172  * This will lookup a name from the cache and return the value if available.
00173  * If there is no entry in the cache this will order a concurrent lookup of the
00174  * name an return a failure.
00175  * @param name name to resolve
00176  * @param addr contains a pointer to the address record upon return, this record
00177  * is in the cache, so you may not free the resulting address! The address is
00178  * always of type struct sockaddr_in (IPv4) at the moment.
00179  * @param addrlen contains the length of addr in bytes upon return
00180  * @return true if resolution was successful, false otherwise
00181  */
00182 bool
00183 NetworkNameResolver::resolve_name(const char *name,
00184                                   struct sockaddr **addr, socklen_t *addrlen)
00185 {
00186   name2addr_cache.lock();
00187 
00188   if ( name2addr_cache.find( (char *)name ) != name2addr_cache.end() ) {
00189     // the name is in the cache, refetch?
00190     std::pair<struct sockaddr *, time_t> &nrec = name2addr_cache[(char *)name];
00191     if ( nrec.second <= time(NULL) ) {
00192       // entry outdated, retry
00193       resolver_thread->resolve_name(name);
00194     }
00195     *addr = nrec.first;
00196     *addrlen = sizeof(struct sockaddr_in);
00197     name2addr_cache.unlock();
00198     return true;
00199   } else {
00200     name2addr_cache.unlock();
00201     resolver_thread->resolve_name(name);
00202     return false;
00203   }
00204 }
00205 
00206 
00207 /** Resolve name and wait for the result.
00208  * This will lookup a name from the cache and return the value if available.
00209  * If there is no entry in the cache this will order a concurrent lookup of the
00210  * name and wait for the result.
00211  * @param name name to resolve
00212  * @param addr contains a pointer to the address record upon return, this record
00213  * is in the cache, so you may not free the resulting address! The address is
00214  * always of type struct sockaddr_in (IPv4) at the moment.
00215  * @param addrlen contains the length of addr in bytes upon return
00216  * @return true if resolution was successful, false otherwise
00217  */
00218 bool
00219 NetworkNameResolver::resolve_name_blocking(const char *name,
00220                                            struct sockaddr **addr, socklen_t *addrlen)
00221 {
00222   if ( resolve_name(name, addr, addrlen) ) {
00223     return true;
00224   } else {
00225     struct sockaddr *_addr;
00226     socklen_t _addrlen;
00227     if ( resolver_thread->resolve_name_immediately(name, &_addr, &_addrlen) ) {
00228       name_resolved(strdup(name), _addr, _addrlen);
00229       *addr = _addr;
00230       *addrlen = _addrlen;
00231       return true;
00232     } else {
00233       return false;
00234     }
00235   }
00236 }
00237 
00238 
00239 /** Resolve address.
00240  * This will lookup an address from the cache and return the value if available.
00241  * If there is no entry in the cache this will order a concurrent lookup of the
00242  * address and return the textual representation of the address.
00243  * @param addr address to resolve
00244  * @param addr_len length of addr in bytes
00245  * @param name contains a pointer to the name upon return. Note that this record
00246  * resides in the cache and may not be freed.
00247  * @return true if resolution was successful, false otherwise
00248  */
00249 bool
00250 NetworkNameResolver::resolve_address(struct sockaddr *addr, socklen_t addr_len, std::string &name)
00251 {
00252   addr2name_cache.lock();
00253   struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
00254 
00255   if ( addr2name_cache.find( saddr->sin_addr.s_addr ) != addr2name_cache.end() ) {
00256     // the name is in the cache, refetch?
00257     std::pair<char *, time_t> &nrec = addr2name_cache[saddr->sin_addr.s_addr];
00258     name = nrec.first;
00259     if ( nrec.second <= time(NULL) ) {
00260       // entry outdated, retry
00261       addr2name_cache.unlock();
00262       resolver_thread->resolve_address(addr, addr_len);
00263     } else {
00264       addr2name_cache.unlock();
00265     }
00266   } else {
00267     char tmp[INET_ADDRSTRLEN];
00268     if ( inet_ntop(AF_INET, &(saddr->sin_addr), tmp, sizeof(tmp)) ) {
00269       char *n = strdup(tmp);
00270 
00271       addr2name_cache[saddr->sin_addr.s_addr] = std::pair<char *, time_t>(n, time(NULL) + __cache_timeout);
00272       name = n;
00273       addr2name_cache.unlock();
00274     } else {
00275       addr2name_cache.unlock();
00276       return false;
00277     }
00278     
00279     resolver_thread->resolve_address(addr, addr_len);
00280   }
00281 
00282   return true;
00283 
00284   /*
00285   char hbuf[NI_MAXHOST];
00286   if ( getnameinfo(addr, addr_len, hbuf, sizeof(hbuf), NULL, 0, 0) == -1 ) {
00287     return false;
00288   } else {
00289     char *tmp = (char *)malloc(strlen(hbuf) + 1);
00290     if ( ! tmp ) {
00291       throw OutOfMemoryException();
00292     }
00293     strcpy(tmp, hbuf);
00294     *name = tmp;
00295     return true;
00296   }
00297   */
00298 }
00299 
00300 
00301 /** Name has been resolved by resolver thread.
00302  * This is an internal function, if you modify it, please make absolutely sure that you
00303  * understand the caches, especially when the key has to be freed! Also note that we
00304  * take over the ownership name and addr and are responsible for freeing at some
00305  * point!
00306  * @param name host name
00307  * @param addr address structure
00308  * @param addrlen length in bytes of addr
00309  */
00310 void
00311 NetworkNameResolver::name_resolved(char *name, struct sockaddr *addr,
00312                                    socklen_t addrlen)
00313 {
00314   name2addr_cache.lock();
00315   if ( (n2acit = name2addr_cache.find( name )) != name2addr_cache.end() ) {
00316     // delete old entry
00317     char *n = (*n2acit).first;
00318     free((*n2acit).second.first);
00319     name2addr_cache.erase(n2acit);
00320     free(n);
00321   }
00322   name2addr_cache[name] = std::pair<struct sockaddr *, time_t>(addr, time(NULL) + __cache_timeout);
00323   name2addr_cache.unlock();
00324 }
00325 
00326 
00327 void
00328 NetworkNameResolver::addr_resolved(struct sockaddr *addr,
00329                                    socklen_t addrlen,
00330                                    char *name, bool namefound)
00331 {
00332   addr2name_cache.lock();
00333   struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
00334   if (namefound) {
00335     if ((a2ncit = addr2name_cache.find( saddr->sin_addr.s_addr )) != addr2name_cache.end() ) {
00336       // delete old entry
00337       free(a2ncit->second.first);
00338       addr2name_cache.erase(a2ncit);
00339       addr2name_cache[saddr->sin_addr.s_addr] = std::pair<char *, time_t>(name, time(NULL) + __cache_timeout);
00340     } else {
00341       free(name);
00342     }
00343   } else {
00344     if ((a2ncit = addr2name_cache.find( saddr->sin_addr.s_addr )) == addr2name_cache.end() ) {
00345       addr2name_cache[saddr->sin_addr.s_addr] = std::pair<char *, time_t>(name, 0);
00346     } else {
00347       free(name);
00348     }
00349   }
00350   free(addr);
00351   addr2name_cache.unlock();
00352 }
00353 
00354 
00355 void
00356 NetworkNameResolver::name_resolution_failed(char *name)
00357 {
00358   free(name);
00359 }
00360 
00361 
00362 void
00363 NetworkNameResolver::address_resolution_failed(struct sockaddr *addr,
00364                                                socklen_t addrlen)
00365 {
00366   free(addr);
00367 }
00368 
00369 
00370 /** Get long hostname.
00371  * @return host name
00372  */
00373 const char *
00374 NetworkNameResolver::hostname()
00375 {
00376   return __host_info->name();
00377 }
00378 
00379 
00380 /** Get short hostname.
00381  * @return short hostname
00382  */
00383 const char *
00384 NetworkNameResolver::short_hostname()
00385 {
00386   return __host_info->short_name();
00387 }
00388 
00389 } // end namespace fawkes