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

Generated on Tue Feb 22 13:31:27 2011 for Fawkes API by  doxygen 1.4.7