Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members  

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 "wvhashtable.h"
00011 #include "wvtcp.h"
00012 #include "wvfork.h"
00013 #include <netdb.h>
00014 #include <sys/types.h>
00015 #include <sys/wait.h>
00016 #include <signal.h>
00017 #include <time.h>
00018 
00019 class WvResolverHost
00020 {
00021 public:
00022     WvString name;
00023     WvIPAddr *addr;
00024     WvIPAddrList addrlist;
00025     bool done, negative;
00026     pid_t pid;
00027     WvLoopback *loop;
00028     time_t last_tried;
00029     
00030     WvResolverHost(const WvString &_name) : name(_name)
00031         { init(); name.unique(); addr = NULL; }
00032     ~WvResolverHost()
00033         {   
00034             // Don't need to delete addr, as deleting the list will take care
00035             // of it.
00036             // if (addr) delete addr;
00037             if (loop) delete loop; 
00038             if (pid)
00039             {
00040                 kill(pid, SIGKILL);
00041                 waitpid(pid, NULL, 0); 
00042             }
00043         }
00044 protected:
00045     WvResolverHost()
00046         { init(); }
00047     void init()
00048         { done = negative = false;
00049           pid = 0; loop = NULL; last_tried = time(NULL); }
00050 };
00051 
00052 class WvResolverAddr : public WvResolverHost
00053 {
00054 public:
00055     WvResolverAddr(WvIPAddr *_addr)
00056         { addr = _addr; }
00057 };
00058 
00059 DeclareWvDict(WvResolverHost, WvString, name);
00060 DeclareWvDict(WvResolverAddr, WvIPAddr, addr[0]);
00061 
00062 // static members of WvResolver
00063 int WvResolver::numresolvers = 0;
00064 WvResolverHostDict *WvResolver::hostmap = NULL;
00065 WvResolverAddrDict *WvResolver::addrmap = NULL;
00066 
00067 
00068 // function that runs in a child task
00069 
00070 static void namelookup(const char *name, WvLoopback *loop)
00071 {
00072     struct hostent *he;
00073     
00074     // wait up to one minute...
00075     alarm(60);
00076     
00077     for (;;)
00078     {
00079         he = gethostbyname(name);
00080         if (he)
00081         {
00082             char **addr = he->h_addr_list;
00083             while (*addr != NULL)
00084             {
00085                 loop->print("%s ", WvIPAddr((unsigned char *)(*addr)));
00086                 addr++;
00087             }
00088             loop->print("\n");
00089             alarm(0);
00090             return;
00091         }
00092         
00093         // not found (yet?)
00094         
00095         if (h_errno != TRY_AGAIN)
00096         {
00097             alarm(0);
00098             return; // not found; blank output
00099         }
00100     }
00101 }
00102 
00103 
00104 WvResolver::WvResolver()
00105 {
00106     numresolvers++;
00107     if (!hostmap)
00108         hostmap = new WvResolverHostDict(10);
00109     if (!addrmap)
00110         addrmap = new WvResolverAddrDict(10);
00111 }
00112 
00113 
00114 WvResolver::~WvResolver()
00115 {
00116     numresolvers--;
00117     if (numresolvers <= 0 && hostmap && addrmap)
00118     {
00119         delete hostmap;
00120         delete addrmap;
00121         hostmap = NULL;
00122         addrmap = NULL;
00123     }
00124 }
00125 
00126 
00127 // returns >0 on success, 0 on not found, -1 on timeout
00128 // If addr==NULL, this just tests to see if the name exists.
00129 int WvResolver::findaddr(int msec_timeout, const WvString &name,
00130                          WvIPAddr const **addr,
00131                          WvIPAddrList *addrlist = NULL)
00132 {
00133     WvResolverHost *host;
00134     time_t now = time(NULL);
00135     int res = 0;
00136 
00137     host = (*hostmap)[name];
00138     if (host)
00139     {
00140         // refresh successes after 5 minutes, retry failures every 1 minute
00141         if ((host->done && host->last_tried + 60*5 < now)
00142             || (!host->done && host->last_tried + 60 < now))
00143         {
00144             hostmap->remove(host);
00145             host = NULL;
00146         }
00147         else if (host->done)
00148         {
00149             if (addr)
00150                 *addr = host->addr;
00151             if (addrlist)
00152             {
00153                 WvIPAddrList::Iter i(host->addrlist);
00154                 for (i.rewind(); i.next(); )
00155                 {
00156                     addrlist->append(i.ptr(), false);
00157                     res++;
00158                 }
00159             }
00160             else
00161                 res = 1;
00162             return res;
00163         }
00164         else if (host->negative)
00165             return 0;
00166     }
00167     
00168     if (!host)
00169     {
00170         host = new WvResolverHost(name);
00171         hostmap->add(host, true);
00172         
00173         host->loop = new WvLoopback();
00174 #define SKIP_FORK_FOR_DEBUGGING 0 // this does NOT work on a real weaver!!
00175 #if SKIP_FORK_FOR_DEBUGGING
00176         namelookup(name, host->loop);
00177 #else
00178         
00179         // don't close host->loop!
00180         host->pid = wvfork(host->loop->getrfd(), host->loop->getwfd());
00181         
00182         if (!host->pid) // child process
00183         {
00184             host->loop->noread();
00185             namelookup(name, host->loop);
00186             _exit(1);
00187         }
00188         host->loop->nowrite();
00189 #endif
00190     }
00191     
00192     // if we get here, we are the parent task waiting for the child.
00193     
00194     do
00195     {
00196         if (waitpid(host->pid, NULL, WNOHANG) == host->pid)
00197             host->pid = 0;
00198         
00199         if (!host->loop->select(msec_timeout < 0 ? 100 : msec_timeout))
00200         {
00201             if (host->pid)
00202             {
00203                 if (msec_timeout >= 0)
00204                     return -1; // timeout, but still trying
00205             }
00206             else
00207             {
00208                 delete host->loop;
00209                 host->loop = NULL;
00210                 host->negative = true;
00211                 return 0; // exited while doing search
00212             }
00213         }
00214         else
00215             break;
00216     } while (host->pid && msec_timeout < 0); // repeat if unlimited timeout!
00217     
00218     // data coming in!
00219     char *line;
00220     
00221     do
00222     {
00223         line = host->loop->getline(-1);
00224     } while (!line && host->loop->isok());
00225     
00226     if (line && line[0] != 0)
00227     {
00228         res = 1;
00229         WvIPAddr *resolvedaddr;
00230         char *p;
00231         p = strtok(line, " \n");
00232         resolvedaddr = new WvIPAddr(p);
00233         host->addr = resolvedaddr;
00234         host->addrlist.append(resolvedaddr, true);
00235         if (addr)
00236             *addr = host->addr;
00237         if (addrlist)
00238             addrlist->append(host->addr, false);
00239         do
00240         {
00241             p = strtok(NULL, " \n");
00242             if(p)
00243             {
00244                 res++;
00245                 resolvedaddr = new WvIPAddr(p);
00246                 host->addrlist.append(resolvedaddr, true);
00247                 if (addrlist)
00248                     addrlist->append(resolvedaddr, false);
00249             }
00250         } while (p);
00251         host->done = true;
00252     }
00253     else
00254         host->negative = true;
00255 
00256     if (host->pid && waitpid(host->pid, NULL, 0) == host->pid)
00257         host->pid = 0;
00258     delete host->loop;
00259     host->loop = NULL;
00260     
00261     // Not lazy anymore!  Return as many addresses as we find.
00262     return host->negative ? 0 : res;
00263 }
00264 
00265 void WvResolver::clearhost(const WvString &hostname)
00266 {
00267     WvResolverHost *host = (*hostmap)[hostname];
00268     if (host)
00269         hostmap->remove(host);
00270 }
00271 
00272 /*
00273 int WvResolver::findname(int msec_timeout, WvIPAddr *ipaddr, char **name)
00274 {
00275     fprintf(stderr, "FIXME: WvResolver::findname() not implemented!\n");
00276     return 0;
00277 }
00278 */
00279 
00280 
00281 bool WvResolver::pre_select(const WvString &hostname,
00282                               WvStream::SelectInfo &si)
00283 {
00284     WvResolverHost *host = (*hostmap)[hostname];
00285     
00286     if (host)
00287     {
00288         if (host->loop)
00289             return host->loop->pre_select(si);
00290         else
00291             return true; // sure thing: already looked up this name!
00292     }
00293     else
00294         return false; // will never be ready... host not even in map!
00295 }

Generated on Sat Aug 24 21:37:01 2002 for WvStreams by doxygen1.2.15