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 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