Fawkes API
Fawkes Development Version
|
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