2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
5 * DNS name resolver with support for background lookups.
7 #include "wvresolver.h"
8 #include "wvloopback.h"
11 #include <sys/types.h>
16 #define WVRESOLVER_SKIP_FORK
19 #define waitpid(a,b,c) (0)
23 #include "wvautoconf.h"
34 WvIPAddrList addrlist
;
40 WvResolverHost(WvStringParm _name
) : name(_name
)
41 { init(); addr
= NULL
; }
45 #ifndef WVRESOLVER_SKIP_FORK
50 // In case a signal is in the process of being delivered...
51 while ((rv
= waitpid(pid
, NULL
, 0)) != pid
)
52 if (rv
== -1 && errno
!= EINTR
)
61 { done
= negative
= false;
62 pid
= 0; loop
= NULL
; last_tried
= time(NULL
); }
65 class WvResolverAddr
: public WvResolverHost
68 WvResolverAddr(WvIPAddr
*_addr
)
72 // static members of WvResolver
73 int WvResolver::numresolvers
= 0;
74 WvResolverHostDict
*WvResolver::hostmap
= NULL
;
75 WvResolverAddrDict
*WvResolver::addrmap
= NULL
;
78 // function that runs in a child task
80 static void namelookup(const char *name
, WvLoopback
*loop
)
84 // wait up to one minute...
87 for (int count
= 0; count
< 10; count
++)
89 he
= gethostbyname(name
);
92 char **addr
= he
->h_addr_list
;
95 loop
->print("%s ", WvIPAddr((unsigned char *)(*addr
)));
105 if (h_errno
!= TRY_AGAIN
)
108 return; // not found; blank output
111 // avoid spinning in a tight loop.
113 // sleep() is documented to possibly mess with the alarm(), so we
114 // have to make sure to reset the alarm here. That's a shame,
115 // because otherwise it would timeout nicely after 60 seconds
116 // overall, not 60 seconds per request.
124 WvResolver::WvResolver()
128 hostmap
= new WvResolverHostDict(10);
130 addrmap
= new WvResolverAddrDict(10);
134 WvResolver::~WvResolver()
137 if (numresolvers
<= 0 && hostmap
&& addrmap
)
147 // returns >0 on success, 0 on not found, -1 on timeout
148 // If addr==NULL, this just tests to see if the name exists.
149 int WvResolver::findaddr(int msec_timeout
, WvStringParm name
,
150 WvIPAddr
const **addr
,
151 WvIPAddrList
*addrlist
)
153 WvResolverHost
*host
;
154 time_t now
= time(NULL
);
157 host
= (*hostmap
)[name
];
161 // refresh successes after 5 minutes, retry failures every 1 minute
162 if ((host
->done
&& host
->last_tried
+ 60*5 < now
)
163 || (!host
->done
&& host
->last_tried
+ 60 < now
))
165 // expired from the cache. Force a repeat lookup below...
166 hostmap
->remove(host
);
171 // entry exists, is marked done, and hasn't expired yet. Return
177 WvIPAddrList::Iter
i(host
->addrlist
);
178 for (i
.rewind(); i
.next(); )
180 addrlist
->append(i
.ptr(), false);
188 else if (host
->negative
)
190 // the entry is in the cache, but the response was negative:
191 // the name doesn't exist.
195 // if we get here, 'host' either exists (still in progress)
196 // or is NULL (need to start again).
201 // nothing matches this hostname in the cache. Create a new entry,
202 // and start a new lookup.
203 host
= new WvResolverHost(name
);
204 hostmap
->add(host
, true);
206 host
->loop
= new WvLoopback();
208 #ifdef WVRESOLVER_SKIP_FORK
209 // background name resolution doesn't work when debugging with gdb!
210 namelookup(name
, host
->loop
);
212 // fork a subprocess so we don't block while doing the DNS lookup.
214 // close everything but host->loop in the subprocess.
215 host
->pid
= wvfork(host
->loop
->getrfd(), host
->loop
->getwfd());
220 host
->loop
->noread();
221 namelookup(name
, host
->loop
);
227 host
->loop
->nowrite();
230 #ifndef WVRESOLVER_SKIP_FORK
232 // if we get here, we are the parent task waiting for the child.
236 if (waitpid(host
->pid
, NULL
, WNOHANG
) == host
->pid
)
239 if (!host
->loop
->select(msec_timeout
< 0 ? 100 : msec_timeout
,
244 if (msec_timeout
>= 0)
245 return -1; // timeout, but still trying
249 // the child is dead. Clean up our stream, too.
250 WVRELEASE(host
->loop
);
252 host
->negative
= true;
253 return 0; // exited while doing search
258 } while (host
->pid
&& msec_timeout
< 0); // repeat if unlimited timeout!
266 line
= host
->loop
->blocking_getline(-1);
267 } while (!line
&& host
->loop
->isok());
269 if (line
&& line
[0] != 0)
272 WvIPAddr
*resolvedaddr
;
274 p
= strtok(line
, " \n");
275 resolvedaddr
= new WvIPAddr(p
);
276 host
->addr
= resolvedaddr
;
277 host
->addrlist
.append(resolvedaddr
, true);
281 addrlist
->append(host
->addr
, false);
284 p
= strtok(NULL
, " \n");
288 resolvedaddr
= new WvIPAddr(p
);
289 host
->addrlist
.append(resolvedaddr
, true);
291 addrlist
->append(resolvedaddr
, false);
297 host
->negative
= true;
299 if (host
->pid
&& waitpid(host
->pid
, NULL
, 0) == host
->pid
)
301 WVRELEASE(host
->loop
);
304 // Return as many addresses as we find.
305 return host
->negative
? 0 : res
;
308 void WvResolver::clearhost(WvStringParm hostname
)
310 WvResolverHost
*host
= (*hostmap
)[hostname
];
312 hostmap
->remove(host
);
316 void WvResolver::pre_select(WvStringParm hostname
, WvStream::SelectInfo
&si
)
318 WvResolverHost
*host
= (*hostmap
)[hostname
];
323 host
->loop
->xpre_select(si
,
324 WvStream::SelectRequest(true, false, false));
326 si
.msec_timeout
= 0; // already ready
331 bool WvResolver::post_select(WvStringParm hostname
, WvStream::SelectInfo
&si
)
333 WvResolverHost
*host
= (*hostmap
)[hostname
];
338 return host
->loop
->xpost_select(si
,
339 WvStream::SelectRequest(true, false, false));
341 return true; // already ready