00001
00002
00003
00004
00005
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 #include "streams.h"
00022 #else
00023 #include "wvautoconf.h"
00024 #include "wvfork.h"
00025 #include <netdb.h>
00026 #include <sys/wait.h>
00027 #endif
00028
00029 class WvResolverHost
00030 {
00031 public:
00032 WvString name;
00033 WvIPAddr *addr;
00034 WvIPAddrList addrlist;
00035 bool done, negative;
00036 pid_t pid;
00037 WvLoopback *loop;
00038 time_t last_tried;
00039
00040 WvResolverHost(WvStringParm _name) : name(_name)
00041 { init(); addr = NULL; }
00042 ~WvResolverHost()
00043 {
00044 WVRELEASE(loop);
00045 #ifndef WVRESOLVER_SKIP_FORK
00046 if (pid && pid != -1)
00047 {
00048 kill(pid, SIGKILL);
00049 pid_t rv;
00050
00051 while ((rv = waitpid(pid, NULL, 0)) != pid)
00052 if (rv == -1 && errno != EINTR)
00053 break;
00054 }
00055 #endif
00056 }
00057 protected:
00058 WvResolverHost()
00059 { init(); }
00060 void init()
00061 { done = negative = false;
00062 pid = 0; loop = NULL; last_tried = time(NULL); }
00063 };
00064
00065 class WvResolverAddr : public WvResolverHost
00066 {
00067 public:
00068 WvResolverAddr(WvIPAddr *_addr)
00069 { addr = _addr; }
00070 };
00071
00072
00073 int WvResolver::numresolvers = 0;
00074 WvResolverHostDict *WvResolver::hostmap = NULL;
00075 WvResolverAddrDict *WvResolver::addrmap = NULL;
00076
00077
00078
00079
00080 static void namelookup(const char *name, WvLoopback *loop)
00081 {
00082 struct hostent *he;
00083
00084
00085 alarm(60);
00086
00087 for (int count = 0; count < 10; count++)
00088 {
00089 he = gethostbyname(name);
00090 if (he)
00091 {
00092 char **addr = he->h_addr_list;
00093 while (*addr != NULL)
00094 {
00095 loop->print("%s ", WvIPAddr((unsigned char *)(*addr)));
00096 addr++;
00097 }
00098 loop->print("\n");
00099 alarm(0);
00100 return;
00101 }
00102
00103
00104
00105 if (h_errno != TRY_AGAIN)
00106 {
00107 alarm(0);
00108 return;
00109 }
00110
00111
00112
00113
00114
00115
00116
00117 sleep(1);
00118
00119 alarm(60);
00120 }
00121 }
00122
00123
00124 WvResolver::WvResolver()
00125 {
00126 numresolvers++;
00127 if (!hostmap)
00128 hostmap = new WvResolverHostDict(10);
00129 if (!addrmap)
00130 addrmap = new WvResolverAddrDict(10);
00131 }
00132
00133
00134 WvResolver::~WvResolver()
00135 {
00136 numresolvers--;
00137 if (numresolvers <= 0 && hostmap && addrmap)
00138 {
00139 delete hostmap;
00140 delete addrmap;
00141 hostmap = NULL;
00142 addrmap = NULL;
00143 }
00144 }
00145
00146
00147
00148
00149 int WvResolver::findaddr(int msec_timeout, WvStringParm name,
00150 WvIPAddr const **addr,
00151 WvIPAddrList *addrlist)
00152 {
00153 WvResolverHost *host;
00154 time_t now = time(NULL);
00155 int res = 0;
00156
00157 host = (*hostmap)[name];
00158
00159 if (host)
00160 {
00161
00162 if ((host->done && host->last_tried + 60*5 < now)
00163 || (!host->done && host->last_tried + 60 < now))
00164 {
00165
00166 hostmap->remove(host);
00167 host = NULL;
00168 }
00169 else if (host->done)
00170 {
00171
00172
00173 if (addr)
00174 *addr = host->addr;
00175 if (addrlist)
00176 {
00177 WvIPAddrList::Iter i(host->addrlist);
00178 for (i.rewind(); i.next(); )
00179 {
00180 addrlist->append(i.ptr(), false);
00181 res++;
00182 }
00183 }
00184 else
00185 res = 1;
00186 return res;
00187 }
00188 else if (host->negative)
00189 {
00190
00191
00192 return 0;
00193 }
00194
00195
00196
00197 }
00198
00199 if (!host)
00200 {
00201
00202
00203 host = new WvResolverHost(name);
00204 hostmap->add(host, true);
00205
00206 host->loop = new WvLoopback();
00207
00208 #ifdef WVRESOLVER_SKIP_FORK
00209
00210 namelookup(name, host->loop);
00211 #else
00212
00213
00214
00215 host->pid = wvfork(host->loop->getrfd(), host->loop->getwfd());
00216
00217 if (!host->pid)
00218 {
00219
00220 host->loop->noread();
00221 namelookup(name, host->loop);
00222 _exit(1);
00223 }
00224 #endif
00225
00226
00227 host->loop->nowrite();
00228 }
00229
00230 #ifndef WVRESOLVER_SKIP_FORK
00231
00232
00233
00234 do
00235 {
00236 if (waitpid(host->pid, NULL, WNOHANG) == host->pid)
00237 host->pid = 0;
00238
00239 if (!host->loop->select(msec_timeout < 0 ? 100 : msec_timeout,
00240 true, false))
00241 {
00242 if (host->pid)
00243 {
00244 if (msec_timeout >= 0)
00245 return -1;
00246 }
00247 else
00248 {
00249
00250 WVRELEASE(host->loop);
00251 host->loop = NULL;
00252 host->negative = true;
00253 return 0;
00254 }
00255 }
00256 else
00257 break;
00258 } while (host->pid && msec_timeout < 0);
00259 #endif
00260
00261
00262 char *line;
00263
00264 do
00265 {
00266 line = host->loop->blocking_getline(-1);
00267 } while (!line && host->loop->isok());
00268
00269 if (line && line[0] != 0)
00270 {
00271 res = 1;
00272 WvIPAddr *resolvedaddr;
00273 char *p;
00274 p = strtok(line, " \n");
00275 resolvedaddr = new WvIPAddr(p);
00276 host->addr = resolvedaddr;
00277 host->addrlist.append(resolvedaddr, true);
00278 if (addr)
00279 *addr = host->addr;
00280 if (addrlist)
00281 addrlist->append(host->addr, false);
00282 do
00283 {
00284 p = strtok(NULL, " \n");
00285 if (p)
00286 {
00287 res++;
00288 resolvedaddr = new WvIPAddr(p);
00289 host->addrlist.append(resolvedaddr, true);
00290 if (addrlist)
00291 addrlist->append(resolvedaddr, false);
00292 }
00293 } while (p);
00294 host->done = true;
00295 }
00296 else
00297 host->negative = true;
00298
00299 if (host->pid && waitpid(host->pid, NULL, 0) == host->pid)
00300 host->pid = 0;
00301 WVRELEASE(host->loop);
00302 host->loop = NULL;
00303
00304
00305 return host->negative ? 0 : res;
00306 }
00307
00308 void WvResolver::clearhost(WvStringParm hostname)
00309 {
00310 WvResolverHost *host = (*hostmap)[hostname];
00311 if (host)
00312 hostmap->remove(host);
00313 }
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324 bool WvResolver::pre_select(WvStringParm hostname,
00325 WvStream::SelectInfo &si)
00326 {
00327 WvResolverHost *host = (*hostmap)[hostname];
00328
00329 if (host)
00330 {
00331 if (host->loop)
00332 return host->loop->xpre_select(si,
00333 WvStream::SelectRequest(true, false, false));
00334 else
00335 return true;
00336 }
00337 else
00338 return false;
00339 }