00001
00002
00003
00004
00005
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
00035
00036
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
00063 int WvResolver::numresolvers = 0;
00064 WvResolverHostDict *WvResolver::hostmap = NULL;
00065 WvResolverAddrDict *WvResolver::addrmap = NULL;
00066
00067
00068
00069
00070 static void namelookup(const char *name, WvLoopback *loop)
00071 {
00072 struct hostent *he;
00073
00074
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
00094
00095 if (h_errno != TRY_AGAIN)
00096 {
00097 alarm(0);
00098 return;
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
00128
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
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
00180 host->pid = wvfork(host->loop->getrfd(), host->loop->getwfd());
00181
00182 if (!host->pid)
00183 {
00184 host->loop->noread();
00185 namelookup(name, host->loop);
00186 _exit(1);
00187 }
00188 host->loop->nowrite();
00189 #endif
00190 }
00191
00192
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;
00205 }
00206 else
00207 {
00208 delete host->loop;
00209 host->loop = NULL;
00210 host->negative = true;
00211 return 0;
00212 }
00213 }
00214 else
00215 break;
00216 } while (host->pid && msec_timeout < 0);
00217
00218
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
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
00274
00275
00276
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;
00292 }
00293 else
00294 return false;
00295 }