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