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 Sat Mar 13 14:55:52 2004 for WvStreams by doxygen 1.3.6-20040222