Make WvStreams compile with gcc 4.4.
[wvstreams.git] / ipstreams / wvresolver.cc
blob7f6e2a10136e07d2a72d4ec4b0ec31f8cb507289
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 *
5 * DNS name resolver with support for background lookups.
6 */
7 #include "wvresolver.h"
8 #include "wvloopback.h"
9 #include "wvaddr.h"
10 #include "wvtcp.h"
11 #include <sys/types.h>
12 #include <signal.h>
13 #include <time.h>
15 #ifdef _WIN32
16 #define WVRESOLVER_SKIP_FORK
17 typedef int pid_t;
18 #define kill(a,b)
19 #define waitpid(a,b,c) (0)
20 #define alarm(a)
21 #include "streams.h"
22 #else
23 #include "wvautoconf.h"
24 #include "wvfork.h"
25 #include <netdb.h>
26 #include <sys/wait.h>
27 #endif
29 class WvResolverHost
31 public:
32 WvString name;
33 WvIPAddr *addr;
34 WvIPAddrList addrlist;
35 bool done, negative;
36 pid_t pid;
37 WvLoopback *loop;
38 time_t last_tried;
40 WvResolverHost(WvStringParm _name) : name(_name)
41 { init(); addr = NULL; }
42 ~WvResolverHost()
44 WVRELEASE(loop);
45 #ifndef WVRESOLVER_SKIP_FORK
46 if (pid && pid != -1)
48 kill(pid, SIGKILL);
49 pid_t rv;
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)
53 break;
55 #endif
57 protected:
58 WvResolverHost()
59 { init(); }
60 void init()
61 { done = negative = false;
62 pid = 0; loop = NULL; last_tried = time(NULL); }
65 class WvResolverAddr : public WvResolverHost
67 public:
68 WvResolverAddr(WvIPAddr *_addr)
69 { addr = _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)
82 struct hostent *he;
84 // wait up to one minute...
85 alarm(60);
87 for (int count = 0; count < 10; count++)
89 he = gethostbyname(name);
90 if (he)
92 char **addr = he->h_addr_list;
93 while (*addr != NULL)
95 loop->print("%s ", WvIPAddr((unsigned char *)(*addr)));
96 addr++;
98 loop->print("\n");
99 alarm(0);
100 return;
103 // not found (yet?)
105 if (h_errno != TRY_AGAIN)
107 alarm(0);
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.
117 sleep(1);
119 alarm(60);
124 WvResolver::WvResolver()
126 numresolvers++;
127 if (!hostmap)
128 hostmap = new WvResolverHostDict(10);
129 if (!addrmap)
130 addrmap = new WvResolverAddrDict(10);
134 WvResolver::~WvResolver()
136 numresolvers--;
137 if (numresolvers <= 0 && hostmap && addrmap)
139 delete hostmap;
140 delete addrmap;
141 hostmap = NULL;
142 addrmap = NULL;
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);
155 int res = 0;
157 host = (*hostmap)[name];
159 if (host)
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);
167 host = NULL;
169 else if (host->done)
171 // entry exists, is marked done, and hasn't expired yet. Return
172 // the cached value.
173 if (addr)
174 *addr = host->addr;
175 if (addrlist)
177 WvIPAddrList::Iter i(host->addrlist);
178 for (i.rewind(); i.next(); )
180 addrlist->append(i.ptr(), false);
181 res++;
184 else
185 res = 1;
186 return res;
188 else if (host->negative)
190 // the entry is in the cache, but the response was negative:
191 // the name doesn't exist.
192 return 0;
195 // if we get here, 'host' either exists (still in progress)
196 // or is NULL (need to start again).
199 if (!host)
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);
211 #else
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());
217 if (!host->pid)
219 // child process
220 host->loop->noread();
221 namelookup(name, host->loop);
222 _exit(1);
224 #endif
226 // parent process
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)
237 host->pid = 0;
239 if (!host->loop->select(msec_timeout < 0 ? 100 : msec_timeout,
240 true, false))
242 if (host->pid)
244 if (msec_timeout >= 0)
245 return -1; // timeout, but still trying
247 else
249 // the child is dead. Clean up our stream, too.
250 WVRELEASE(host->loop);
251 host->loop = NULL;
252 host->negative = true;
253 return 0; // exited while doing search
256 else
257 break;
258 } while (host->pid && msec_timeout < 0); // repeat if unlimited timeout!
259 #endif
261 // data coming in!
262 char *line;
266 line = host->loop->blocking_getline(-1);
267 } while (!line && host->loop->isok());
269 if (line && line[0] != 0)
271 res = 1;
272 WvIPAddr *resolvedaddr;
273 char *p;
274 p = strtok(line, " \n");
275 resolvedaddr = new WvIPAddr(p);
276 host->addr = resolvedaddr;
277 host->addrlist.append(resolvedaddr, true);
278 if (addr)
279 *addr = host->addr;
280 if (addrlist)
281 addrlist->append(host->addr, false);
284 p = strtok(NULL, " \n");
285 if (p)
287 res++;
288 resolvedaddr = new WvIPAddr(p);
289 host->addrlist.append(resolvedaddr, true);
290 if (addrlist)
291 addrlist->append(resolvedaddr, false);
293 } while (p);
294 host->done = true;
296 else
297 host->negative = true;
299 if (host->pid && waitpid(host->pid, NULL, 0) == host->pid)
300 host->pid = 0;
301 WVRELEASE(host->loop);
302 host->loop = NULL;
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];
311 if (host)
312 hostmap->remove(host);
316 void WvResolver::pre_select(WvStringParm hostname, WvStream::SelectInfo &si)
318 WvResolverHost *host = (*hostmap)[hostname];
320 if (host)
322 if (host->loop)
323 host->loop->xpre_select(si,
324 WvStream::SelectRequest(true, false, false));
325 else
326 si.msec_timeout = 0; // already ready
331 bool WvResolver::post_select(WvStringParm hostname, WvStream::SelectInfo &si)
333 WvResolverHost *host = (*hostmap)[hostname];
335 if (host)
337 if (host->loop)
338 return host->loop->xpost_select(si,
339 WvStream::SelectRequest(true, false, false));
340 else
341 return true; // already ready
343 return false;