Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

wvresolver.cc

Go to the documentation of this file.
00001 /* 00002 * Worldvisions Weaver Software: 00003 * Copyright (C) 1997-2002 Net Integration Technologies, Inc. 00004 * 00005 * DNS name resolver with support for background lookups. 00006 */ 00007 #include "wvresolver.h" 00008 #include "wvloopback.h" 00009 #include "wvaddr.h" 00010 #include "wvtcp.h" 00011 #include <sys/types.h> 00012 #include <signal.h> 00013 #include <time.h> 00014 00015 #ifdef _WIN32 00016 #define WVRESOLVER_SKIP_FORK 00017 typedef int pid_t; 00018 #define kill(a,b) 00019 #define waitpid(a,b,c) (0) 00020 #define alarm(a) 00021 #else 00022 #include "wvautoconf.h" 00023 #include "wvfork.h" 00024 #include <netdb.h> 00025 #include <sys/wait.h> 00026 #endif 00027 00028 class WvResolverHost 00029 { 00030 public: 00031 WvString name; 00032 WvIPAddr *addr; 00033 WvIPAddrList addrlist; 00034 bool done, negative; 00035 pid_t pid; 00036 WvLoopback *loop; 00037 time_t last_tried; 00038 00039 WvResolverHost(WvStringParm _name) : name(_name) 00040 { init(); addr = NULL; } 00041 ~WvResolverHost() 00042 { 00043 if (loop) delete loop; 00044 if (pid && pid != -1) 00045 { 00046 kill(pid, SIGKILL); 00047 waitpid(pid, NULL, 0); 00048 } 00049 } 00050 protected: 00051 WvResolverHost() 00052 { init(); } 00053 void init() 00054 { done = negative = false; 00055 pid = 0; loop = NULL; last_tried = time(NULL); } 00056 }; 00057 00058 class WvResolverAddr : public WvResolverHost 00059 { 00060 public: 00061 WvResolverAddr(WvIPAddr *_addr) 00062 { addr = _addr; } 00063 }; 00064 00065 // static members of WvResolver 00066 int WvResolver::numresolvers = 0; 00067 WvResolverHostDict *WvResolver::hostmap = NULL; 00068 WvResolverAddrDict *WvResolver::addrmap = NULL; 00069 00070 00071 // function that runs in a child task 00072 00073 static void namelookup(const char *name, WvLoopback *loop) 00074 { 00075 struct hostent *he; 00076 00077 // wait up to one minute... 00078 alarm(60); 00079 00080 for (;;) 00081 { 00082 he = gethostbyname(name); 00083 if (he) 00084 { 00085 char **addr = he->h_addr_list; 00086 while (*addr != NULL) 00087 { 00088 loop->print("%s ", WvIPAddr((unsigned char *)(*addr))); 00089 addr++; 00090 } 00091 loop->print("\n"); 00092 alarm(0); 00093 return; 00094 } 00095 00096 // not found (yet?) 00097 00098 if (h_errno != TRY_AGAIN) 00099 { 00100 alarm(0); 00101 return; // not found; blank output 00102 } 00103 } 00104 } 00105 00106 00107 WvResolver::WvResolver() 00108 { 00109 numresolvers++; 00110 if (!hostmap) 00111 hostmap = new WvResolverHostDict(10); 00112 if (!addrmap) 00113 addrmap = new WvResolverAddrDict(10); 00114 } 00115 00116 00117 WvResolver::~WvResolver() 00118 { 00119 numresolvers--; 00120 if (numresolvers <= 0 && hostmap && addrmap) 00121 { 00122 delete hostmap; 00123 delete addrmap; 00124 hostmap = NULL; 00125 addrmap = NULL; 00126 } 00127 } 00128 00129 00130 // returns >0 on success, 0 on not found, -1 on timeout 00131 // If addr==NULL, this just tests to see if the name exists. 00132 int WvResolver::findaddr(int msec_timeout, WvStringParm name, 00133 WvIPAddr const **addr, 00134 WvIPAddrList *addrlist) 00135 { 00136 WvResolverHost *host; 00137 time_t now = time(NULL); 00138 int res = 0; 00139 00140 host = (*hostmap)[name]; 00141 00142 if (host) 00143 { 00144 // refresh successes after 5 minutes, retry failures every 1 minute 00145 if ((host->done && host->last_tried + 60*5 < now) 00146 || (!host->done && host->last_tried + 60 < now)) 00147 { 00148 // expired from the cache. Force a repeat lookup below... 00149 hostmap->remove(host); 00150 host = NULL; 00151 } 00152 else if (host->done) 00153 { 00154 // entry exists, is marked done, and hasn't expired yet. Return 00155 // the cached value. 00156 if (addr) 00157 *addr = host->addr; 00158 if (addrlist) 00159 { 00160 WvIPAddrList::Iter i(host->addrlist); 00161 for (i.rewind(); i.next(); ) 00162 { 00163 addrlist->append(i.ptr(), false); 00164 res++; 00165 } 00166 } 00167 else 00168 res = 1; 00169 return res; 00170 } 00171 else if (host->negative) 00172 { 00173 // the entry is in the cache, but the response was negative: 00174 // the name doesn't exist. 00175 return 0; 00176 } 00177 00178 // if we get here, 'host' either exists (still in progress) 00179 // or is NULL (need to start again). 00180 } 00181 00182 if (!host) 00183 { 00184 // nothing matches this hostname in the cache. Create a new entry, 00185 // and start a new lookup. 00186 host = new WvResolverHost(name); 00187 hostmap->add(host, true); 00188 00189 host->loop = new WvLoopback(); 00190 00191 #ifdef WVRESOLVER_SKIP_FORK 00192 // background name resolution doesn't work when debugging with gdb! 00193 namelookup(name, host->loop); 00194 #else 00195 // fork a subprocess so we don't block while doing the DNS lookup. 00196 00197 // close everything but host->loop in the subprocess. 00198 host->pid = wvfork(host->loop->getrfd(), host->loop->getwfd()); 00199 00200 if (!host->pid) 00201 { 00202 // child process 00203 host->loop->noread(); 00204 namelookup(name, host->loop); 00205 _exit(1); 00206 } 00207 #endif 00208 00209 // parent process 00210 host->loop->nowrite(); 00211 } 00212 00213 #ifndef WVRESOLVER_SKIP_FORK 00214 00215 // if we get here, we are the parent task waiting for the child. 00216 00217 do 00218 { 00219 if (waitpid(host->pid, NULL, WNOHANG) == host->pid) 00220 host->pid = 0; 00221 00222 if (!host->loop->select(msec_timeout < 0 ? 100 : msec_timeout, 00223 true, false)) 00224 { 00225 if (host->pid) 00226 { 00227 if (msec_timeout >= 0) 00228 return -1; // timeout, but still trying 00229 } 00230 else 00231 { 00232 // the child is dead. Clean up our stream, too. 00233 delete host->loop; 00234 host->loop = NULL; 00235 host->negative = true; 00236 return 0; // exited while doing search 00237 } 00238 } 00239 else 00240 break; 00241 } while (host->pid && msec_timeout < 0); // repeat if unlimited timeout! 00242 #endif 00243 00244 // data coming in! 00245 char *line; 00246 00247 do 00248 { 00249 line = host->loop->getline(-1); 00250 } while (!line && host->loop->isok()); 00251 00252 if (line && line[0] != 0) 00253 { 00254 res = 1; 00255 WvIPAddr *resolvedaddr; 00256 char *p; 00257 p = strtok(line, " \n"); 00258 resolvedaddr = new WvIPAddr(p); 00259 host->addr = resolvedaddr; 00260 host->addrlist.append(resolvedaddr, true); 00261 if (addr) 00262 *addr = host->addr; 00263 if (addrlist) 00264 addrlist->append(host->addr, false); 00265 do 00266 { 00267 p = strtok(NULL, " \n"); 00268 if (p) 00269 { 00270 res++; 00271 resolvedaddr = new WvIPAddr(p); 00272 host->addrlist.append(resolvedaddr, true); 00273 if (addrlist) 00274 addrlist->append(resolvedaddr, false); 00275 } 00276 } while (p); 00277 host->done = true; 00278 } 00279 else 00280 host->negative = true; 00281 00282 if (host->pid && waitpid(host->pid, NULL, 0) == host->pid) 00283 host->pid = 0; 00284 delete host->loop; 00285 host->loop = NULL; 00286 00287 // Return as many addresses as we find. 00288 return host->negative ? 0 : res; 00289 } 00290 00291 void WvResolver::clearhost(WvStringParm hostname) 00292 { 00293 WvResolverHost *host = (*hostmap)[hostname]; 00294 if (host) 00295 hostmap->remove(host); 00296 } 00297 00298 /* 00299 int WvResolver::findname(int msec_timeout, WvIPAddr *ipaddr, char **name) 00300 { 00301 fprintf(stderr, "FIXME: WvResolver::findname() not implemented!\n"); 00302 return 0; 00303 } 00304 */ 00305 00306 00307 bool WvResolver::pre_select(WvStringParm hostname, 00308 WvStream::SelectInfo &si) 00309 { 00310 WvResolverHost *host = (*hostmap)[hostname]; 00311 00312 if (host) 00313 { 00314 if (host->loop) 00315 return host->loop->xpre_select(si, 00316 WvStream::SelectRequest(true, false, false)); 00317 else 00318 return true; // sure thing: already looked up this name! 00319 } 00320 else 00321 return false; // will never be ready... host not even in map! 00322 }

Generated on Tue Oct 5 01:09:20 2004 for WvStreams by doxygen 1.3.7