home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
-
- /*
- * unix-dns.c --- portable nonblocking DNS for Unix
- * Created: Jamie Zawinski <jwz@netscape.com>, 19-Dec-96.
- */
-
- #if defined(XP_UNIX) && defined(UNIX_ASYNC_DNS)
-
- static void dns_socks_kludge(void);
-
- /* Todo:
-
- = Should we actually use the CHANGING_ARGV_WORKS code, on systems where
- that is true, or just always do the exec() hack? If we use it, it
- needs to be debugged more, and the exact set of systems on which it
- works needs to be determined. (Works on: SunOS 4, Linux, AIX, OSF;
- doesn't work on: Solaris, Irix, UnixWare, HPUX.) (But HPUX has an
- undocumented ioctl that lets you change a proc's name.) (Unix sucks
- a lot.)
- */
-
-
- /* Compile-time options:
- -DDNS_EXPLICITLY_FLUSH_PIPES
- to do ioctls() to flush out the pipes after each write().
- at one point I thought I needed this, but it doesn't
- actually seem to be necessary.
-
- -DGETHOSTBYNAME_DELAY=N
- to insert an artificial delay of N seconds before each
- call to gethostbyname (in order to simulate DNS lossage.)
-
- -DNO_SOCKS_NS_KLUDGE
- Set this to *disable* the $SOCKS_NS kludge. Otherwise,
- that environment variable will be consulted for use as an
- alternate DNS root. It's historical; don't ask me...
-
- -DRANDOMIZE_ADDRESSES
- This code only deals with one IP address per host; if this
- is set, then that host will be randomly selected from among
- all the addresses of the host; otherwise, it will be the
- first address.
-
- -DCHANGING_ARGV_WORKS
- (This option isn't fully implemented yet, and can't work
- on some systems anyway.)
-
- The code in this file wants to fork(), and then change the
- name of the second process as reported by `ps'. Some systems
- let you do that by overwriting argv[0], but some do not.
-
- If this isn't defined, we effect this change by re-execing
- after the fork (since you can pass a new name that way.)
- This depends on exec(argv[0] ... ) working, which it might
- not if argv[0] has a non-absolute path in it, and the user
- has played stupid games with $PATH or something. Those
- users probably deserve to lose.
-
- -DSTANDALONE
- to include a main() for interactive command-line testing.
-
- -DPROC3_DEBUG_PRINT
- to cause proc3 (the gethostbyname() processes) to print
- diagnostics to stderr.
-
- -DPROC2_DEBUG_PRINT
- to cause proc2 (the looping, dispatching process) to print
- diagnostics to stderr.
-
- -DPROC1_DEBUG_PRINT
- to cause proc1 (the main thread) to print diagnostics to
- stderr. */
-
-
- #include "unix-dns.h"
-
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdio.h>
- #include <assert.h>
- #include <netdb.h> /* for gethostbyname() */
- #include <signal.h> /* for kill() */
- #include <sys/param.h> /* for MAXHOSTNAMELEN */
- #include <sys/wait.h> /* for waitpid() */
- #include <sys/time.h> /* for gettimeofday() */
- #include <signal.h> /* for signal() and the signal names */
-
- #ifdef AIX
- #include <sys/select.h> /* for fd_set */
- #endif
-
- #ifndef NO_SOCKS_NS_KLUDGE
- # include <netinet/in.h> /* for sockaddr_in (from inet.h) */
- # include <arpa/inet.h> /* for in_addr (from nameser.h) */
- # include <arpa/nameser.h> /* for MAXDNAME (from resolv.h) */
- # include <resolv.h> /* for res_init() and _res */
- #endif
-
- #if !defined(__irix)
- /* Looks like Irix is the only one that has getdtablehi()? */
- # define getdtablehi() getdtablesize()
- #endif
-
- #if !defined(__irix)
- /* Looks like Irix and Solaris 5.5 are the only ones that have
- getdtablesize()... but since Solaris 5.4 doesn't have it,
- Solaris is out. (If you find a system that doesn't even
- have FD_SETSIZE, just grab your ankles and set it to 255.)
- */
- # define getdtablesize() (FD_SETSIZE)
- #endif
-
-
- #ifdef DNS_EXPLICITLY_FLUSH_PIPES
- # include <stropts.h> /* for I_FLUSH and FLUSHRW */
- #endif
-
- /* Rumor has it that some systems lie about this value, and actually accept
- host names longer than sys/param.h claims. But it's definitely the case
- that some systems (and again I don't know which) crash if you pass a
- too-long hostname into gethostbyname(). So better safe than sorry.
- */
- #ifndef MAXHOSTNAMELEN
- # define MAXHOSTNAMELEN 64
- #endif
-
- #define ASSERT(x) assert(x)
-
- #ifdef DNS_EXPLICITLY_FLUSH_PIPES
- # define FLUSH(fd) ioctl(fd, I_FLUSH, FLUSHRW)
- #else
- # define FLUSH(x)
- #endif
-
- /* We use fdopen() to get the more convenient stdio interface on the pipes
- we use; but we don't want the stdio library to provide *any* buffering;
- just use the buffering that the pipe itself provides, so that when
- select() says there is no input available, fgetc() agrees.
- */
- #define SET_BUFFERING(file) setvbuf((file), NULL, _IONBF, 0)
-
-
- #ifdef RANDOMIZE_ADDRESSES
- /* Unix is Random. */
- # if defined(UNIXWARE) || defined(_INCLUDE_HPUX_SOURCE) || (defined(__sun) && defined(__svr4__))
- # define RANDOM rand
- # define SRANDOM srand
- # else /* !BSD-incompatible-Unixes */
- # define RANDOM random
- # define SRANDOM srandom
- # endif /* !BSD-incompatible-Unixes */
- #endif /* RANDOMIZE_ADDRESSES */
-
-
- /* Way kludgy debugging/logging interface, since gdb's support
- for debugging fork'ed processes is pathetic.
- */
- #define LOG_PROCn(PROC,PREFIX,BUF,SUFFIX,QL) do{\
- fprintf(stderr, \
- "\t" PROC " (%lu): " PREFIX ": (ql=%ld) %s" SUFFIX, \
- ((unsigned long) getpid()), QL, BUF); \
- } while(0)
-
- #ifdef PROC3_DEBUG_PRINT
- # define LOG_PROC3(PREFIX,BUF,SUFFIX) LOG_PROCn("proc3",PREFIX,BUF,SUFFIX,0L)
- #else
- # define LOG_PROC3(PREFIX,BUF,SUFFIX)
- #endif
-
- #ifdef PROC2_DEBUG_PRINT
- # define LOG_PROC2(PREFIX,BUF,SUFFIX) LOG_PROCn("proc2",PREFIX,BUF,SUFFIX,\
- proc2_queue_length())
- #else
- # define LOG_PROC2(PREFIX,BUF,SUFFIX)
- #endif
-
- #ifdef PROC1_DEBUG_PRINT
- # define LOG_PROC1(PREFIX,BUF,SUFFIX) LOG_PROCn("proc1",PREFIX,BUF,SUFFIX,\
- proc1_queue_length())
- #else
- # define LOG_PROC1(PREFIX,BUF,SUFFIX)
- #endif
-
-
- /* The internal status codes that are used; these follow the basic
- SMTP/NNTP model of three-digit codes.
- */
- #define DNS_STATUS_GETHOSTBYNAME_OK 101 /* proc3 -> proc2 */
- #define DNS_STATUS_LOOKUP_OK 102 /* proc2 -> proc1 */
- #define DNS_STATUS_KILLED_OK 103 /* proc2 -> proc1 */
- #define DNS_STATUS_LOOKUP_STARTED 201 /* proc2 -> proc1 */
- #define DNS_STATUS_GETHOSTBYNAME_FAILED 501 /* proc3 -> proc2 */
- #define DNS_STATUS_LOOKUP_FAILED 502 /* proc2 -> proc1 */
- #define DNS_STATUS_LOOKUP_NOT_STARTED 503 /* proc2 -> proc1 */
- #define DNS_STATUS_KILL_FAILED 504 /* proc2 -> proc1 */
- #define DNS_STATUS_UNIMPLEMENTED 601 /* unimplemented (ha ha) */
- #define DNS_STATUS_INTERNAL_ERROR 602 /* assertion failure */
-
- #define DNS_PROC2_NAME "(dns helper)"
-
-
- static char *
- string_trim(char *s)
- {
- char *s2;
- if (!s) return 0;
- s2 = s + strlen(s) - 1;
- while (s2 > s && (*s2 == '\n' || *s2 == '\r' || *s2 == ' ' || *s2 == '\t'))
- *s2-- = 0;
- while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
- s++;
- return s;
- }
-
-
- /* Process 3.
- Inherits a string (hostname) from the spawner.
- Writes "1xx: a.b.c.d\n" or "5xx: error msg\n".
- */
-
- static void
- blocking_gethostbyname (const char *name, int out_fd,
- unsigned long random_number)
- {
- char buf[MAXHOSTNAMELEN + 100];
- struct hostent *h;
- unsigned long which_addr = 0;
-
- static int firstTime=1;
- if (firstTime) {
- firstTime=0;
- dns_socks_kludge();
- }
-
- #if GETHOSTBYNAME_DELAY
- {
- int i = GETHOSTBYNAME_DELAY;
- LOG_PROC3("sleeping","","\n");
- sleep(i);
- }
- #endif /* GETHOSTBYNAME_DELAY */
-
- LOG_PROC3("gethostbyname",name,"\n");
-
- h = gethostbyname(name);
-
- #ifdef RANDOMIZE_ADDRESSES
- if (h)
- {
- unsigned long n_addrs;
- for (n_addrs = 0; h->h_addr_list[n_addrs]; n_addrs++)
- ;
- if (n_addrs > 0)
- which_addr = (random_number % n_addrs);
- else
- h = 0;
- }
- #endif /* RANDOMIZE_ADDRESSES */
-
- if (h)
- sprintf(buf, "%d: %d.%d.%d.%d\n",
- DNS_STATUS_GETHOSTBYNAME_OK,
- ((unsigned char *) h->h_addr_list[which_addr])[0],
- ((unsigned char *) h->h_addr_list[which_addr])[1],
- ((unsigned char *) h->h_addr_list[which_addr])[2],
- ((unsigned char *) h->h_addr_list[which_addr])[3]);
- else
- sprintf(buf, "%d: host %s not found\n",
- DNS_STATUS_GETHOSTBYNAME_FAILED, name);
-
- LOG_PROC3("writing response",buf,"");
-
- write(out_fd, buf, strlen(buf));
- FLUSH(out_fd);
- }
-
-
-
- /* Process 2.
- Loops forever.
-
- Reads "lookup: hostname\n".
- Writes "2xx: id\n" or "5xx: error msg\n".
- Some time later, writes "1xx: id: a.b.c.d\n" or "5xx: id: error msg\n".
-
- -or-
-
- Reads "kill: id\n".
- Writes "1xx: killed" or "5xx: kill failed"
- */
-
- typedef struct dns_lookup {
- long id;
- pid_t pid;
- FILE *fd;
- char *name;
- struct dns_lookup *next, *prev;
- } dns_lookup;
-
- static long dns_id_tick;
- static dns_lookup *proc2_queue = 0;
-
-
- static long
- proc2_queue_length(void)
- {
- dns_lookup *obj;
- long i = 0;
- for (obj = proc2_queue; obj; obj = obj->next) i++;
- return i;
- }
-
-
- static dns_lookup *
- new_lookup_object (const char *name)
- {
- dns_lookup *obj;
- char *n2;
- ASSERT(name);
- if (!name) return 0;
-
- n2 = strdup(name);
- if (!n2) return 0; /* MK_OUT_OF_MEMORY */
-
- obj = (dns_lookup *) malloc(sizeof(*obj));
- if (!obj) return 0;
- memset(obj, 0, sizeof(*obj));
- obj->id = ++dns_id_tick;
- obj->name = n2;
- obj->fd = 0;
-
- ASSERT(!proc2_queue || proc2_queue->prev == 0);
- obj->next = proc2_queue;
- if (proc2_queue)
- proc2_queue->prev = obj;
- proc2_queue = obj;
-
- return obj;
- }
-
-
- /* Frees the object associated with a host lookup on which proc2 is waiting,
- and writes the result to proc1. Returns 0 if everything went ok, negative
- otherwise. Status should be 0 (for "ok") or negative (the inverse of one
- of the three-digit response codes that *proc1* is expecting (not the codes
- that *proc3* returns.)n
- */
- static int
- free_lookup_object (dns_lookup *obj,
- int out_fd,
- int status, const char *error_msg,
- const unsigned char ip_addr[4])
- {
- char buf[MAXHOSTNAMELEN + 100];
-
- ASSERT(status == 0 || (status < -100 && status > -700));
-
- ASSERT(obj);
- if (!obj) return -1;
-
- if (status >= 0 && ip_addr)
- {
- sprintf(buf, "%d: %lu: %d.%d.%d.%d\n",
- DNS_STATUS_LOOKUP_OK,
- obj->id, ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
- }
- else
- {
- ASSERT(error_msg);
- sprintf(buf, "%d: %lu: %s\n",
- ((status < -100) ? -status : DNS_STATUS_INTERNAL_ERROR),
- obj->id, (error_msg ? error_msg : "???"));
- }
-
- LOG_PROC2("writing response",buf,"");
-
- write(out_fd, buf, strlen(buf));
- FLUSH(out_fd);
-
- if (obj->fd)
- {
- fclose(obj->fd);
- obj->fd = 0;
- }
- if (obj->prev)
- {
- ASSERT(obj->prev->next != obj->next);
- obj->prev->next = obj->next;
- }
- if (obj->next)
- {
- ASSERT(obj->next->prev != obj->prev);
- obj->next->prev = obj->prev;
- }
- if (proc2_queue == obj)
- proc2_queue = obj->next;
-
- memset(obj, ~0, sizeof(obj));
- free(obj);
-
- return 0;
- }
-
-
- static dns_lookup *
- spawn_lookup_process (const char *name, int out_fd)
- {
- dns_lookup *obj = new_lookup_object(name);
- pid_t forked;
- int fds[2]; /* one reads from [0] and writes to [1]. */
- unsigned long random_number = 0;
-
- if (pipe (fds))
- {
- free_lookup_object(obj, out_fd,
- -DNS_STATUS_LOOKUP_NOT_STARTED,
- "can't make pipe", 0);
- obj = 0;
- return 0;
- }
-
- /* Save the fd from which we should read from the forked proc. */
- obj->fd = fdopen(fds[0], "r");
- if (!obj->fd)
- {
- close(fds[0]);
- close(fds[1]);
- free_lookup_object(obj, out_fd,
- -DNS_STATUS_LOOKUP_NOT_STARTED,
- "out of memory", 0);
- obj = 0;
- return 0;
- }
- SET_BUFFERING(obj->fd);
-
- #ifdef RANDOMIZE_ADDRESSES
- /* Generate the random numbers in proc2 for use by proc3, so that the
- PRNG actually gets permuted. */
- random_number = (unsigned long) RANDOM();
- #endif /* RANDOMIZE_ADDRESSES */
-
-
- switch (forked = fork ())
- {
- case -1:
- {
- close (fds[1]);
- free_lookup_object(obj, out_fd,
- -DNS_STATUS_LOOKUP_NOT_STARTED,
- "can't fork", 0);
- obj = 0;
- return 0;
- }
- break;
-
- case 0: /* This is the forked process. */
- {
- /* Close the other side of the pipe (it's used by the main fork.) */
- close (fds[0]);
-
- /* Call gethostbyname, then write the result down the pipe. */
- blocking_gethostbyname(name, fds[1], random_number);
-
- /* Now exit this process. */
- close (fds[1]);
- exit(0);
- }
- break;
-
- default:
- {
- /* This is the "old" process (subproc pid is in `forked'.) */
- obj->pid = forked;
-
- /* This is the file descriptor we created for the benefit
- of the child process - we don't need it in the parent. */
- close (fds[1]);
-
- return obj; /* ok. */
- }
- break;
- }
-
- /* shouldn't get here. */
- ASSERT(0);
- return 0;
- }
-
-
- /* Given a line which is an asynchronous response from proc3, find the
- associated lookup object, feed the response on to proc1, and free
- the dns_lookup object.
- */
- static void
- handle_subproc_response(dns_lookup *obj, const char *line, int out_fd)
- {
- int iip[4];
- char *s;
- int code = 0;
- int i;
-
- i = sscanf(line, "%d: %d.%d.%d.%d\n",
- &code, &iip[0], &iip[1], &iip[2], &iip[3]);
- if (i != 5)
- {
- i = sscanf(line, "%d:", &code);
- if (i != 1 || code <= 100)
- code = DNS_STATUS_INTERNAL_ERROR;
- }
-
- switch (code)
- {
- case DNS_STATUS_GETHOSTBYNAME_OK:
- {
- unsigned char ip[5];
- ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3];
- ip[4] = 0; /* just in case */
- free_lookup_object (obj, out_fd, 0, 0, ip);
- obj = 0;
- break;
- }
-
- case DNS_STATUS_GETHOSTBYNAME_FAILED:
- default:
- {
- ASSERT(code == DNS_STATUS_GETHOSTBYNAME_FAILED);
- if (code == DNS_STATUS_GETHOSTBYNAME_FAILED)
- code = DNS_STATUS_LOOKUP_FAILED;
- s = strchr(line, ':');
- if (s) s++;
- free_lookup_object (obj, out_fd,
- (code > 100
- ? -code
- : -DNS_STATUS_INTERNAL_ERROR),
- (s ? string_trim(s) : "???"), 0);
- obj = 0;
- break;
- }
- }
- }
-
-
- static void
- cancel_lookup(long id, int out_fd)
- {
- dns_lookup *obj = 0;
-
- if (proc2_queue)
- for (obj = proc2_queue; obj; obj = obj->next)
- if (obj->id == id) break;
-
- if (obj)
- {
- if (obj->pid)
- {
- pid_t pid2;
-
- /*
- * SIGKILL causes the browser to hang if the user clicks on the stop
- * button while a long/bogus dns lookup is in progress. According
- * to signal guru asharma, we should use SIGQUIT instead. -re
- */
- /* kill(obj->pid, SIGKILL); */
- kill(obj->pid, SIGQUIT);
- pid2 = waitpid(obj->pid, 0, 0);
- ASSERT(obj->pid == pid2);
- }
- free_lookup_object(obj, out_fd, -DNS_STATUS_KILLED_OK, "cancelled", 0);
- obj = 0;
- }
- else
- {
- char buf[200];
- sprintf (buf, "%d: %lu: unable to cancel\n", DNS_STATUS_KILL_FAILED, id);
- LOG_PROC2("writing (kill) response",buf,"");
- write(out_fd, buf, strlen(buf));
- }
-
- FLUSH(out_fd);
- }
-
-
- static void
- dns_socks_kludge(void)
- {
- #ifndef NO_SOCKS_NS_KLUDGE
- /* Gross historical kludge.
- If the environment variable $SOCKS_NS is defined, stomp on the host that
- the DNS code uses for host lookup to be a specific ip address.
- */
- char *ns = getenv("SOCKS_NS");
- if (ns && *ns)
- {
- /* Gross hack added to Gross historical kludge - need to
- * initialize resolv.h structure first with low-cost call
- * to gethostbyname() before hacking _res. Subsequent call
- * to gethostbyname() will then pick up $SOCKS_NS address.
- */
- gethostbyname("localhost");
-
- res_init();
- _res.nsaddr_list[0].sin_addr.s_addr = inet_addr(ns);
- _res.nscount = 1;
- }
- #endif /* !NO_SOCKS_NS_KLUDGE */
- }
-
-
- #ifdef PROC2_DEBUG_PRINT
- static void
- dns_signal_handler_dfl (int sig)
- {
- char buf[100];
- signal (sig, SIG_DFL);
- sprintf(buf,"caught signal %d!\n",sig);
- LOG_PROC2("dns_signal_handler",buf,"");
- kill (getpid(), sig);
- }
- #endif /* PROC2_DEBUG_PRINT */
-
- static void
- dns_signal_handler_ign (int sig)
- {
- #ifdef PROC2_DEBUG_PRINT
- char buf[100];
- sprintf(buf,"caught signal %d -- ignoring.\n",sig);
- LOG_PROC2("dns_signal_handler",buf,"");
- #endif /* PROC2_DEBUG_PRINT */
- signal (sig, dns_signal_handler_ign);
- }
-
-
- static void
- dns_catch_signals(void)
- {
- #ifndef SIG_ERR
- # define SIG_ERR -1
- #endif
-
- #ifdef PROC2_DEBUG_PRINT
- int sig;
- char buf[255];
- LOG_PROC2("dns_catch_signals","plugging in...\n","");
- # define CATCH_SIGNAL_DFL(SIG) sig = SIG; \
- if (((int)signal(SIG, dns_signal_handler_dfl)) == ((int)SIG_ERR)) \
- goto FAIL
- # define CATCH_SIGNAL_IGN(SIG) sig = SIG; \
- if (((int)signal(SIG, dns_signal_handler_ign)) == ((int)SIG_ERR)) \
- goto FAIL
- #else /* !PROC2_DEBUG_PRINT */
- # define CATCH_SIGNAL_DFL(SIG) \
- if ((int)signal(SIG,SIG_DFL) == ((int)SIG_ERR)) goto FAIL
- # define CATCH_SIGNAL_IGN(SIG) \
- if ((int)signal(SIG,SIG_IGN) == ((int)SIG_ERR)) goto FAIL
- #endif /* !PROC2_DEBUG_PRINT */
-
- CATCH_SIGNAL_IGN(SIGHUP);
- CATCH_SIGNAL_IGN(SIGINT);
- CATCH_SIGNAL_IGN(SIGQUIT);
- CATCH_SIGNAL_DFL(SIGILL);
- CATCH_SIGNAL_DFL(SIGTRAP);
- CATCH_SIGNAL_DFL(SIGIOT);
- CATCH_SIGNAL_DFL(SIGABRT);
- # ifdef SIGEMT
- CATCH_SIGNAL_DFL(SIGEMT);
- # endif
- CATCH_SIGNAL_DFL(SIGFPE);
- CATCH_SIGNAL_DFL(SIGBUS);
- CATCH_SIGNAL_DFL(SIGSEGV);
- # ifdef SIGSYS
- CATCH_SIGNAL_DFL(SIGSYS);
- # endif
- CATCH_SIGNAL_DFL(SIGPIPE);
- CATCH_SIGNAL_DFL(SIGTERM); /* ignore this one? hmm... */
- CATCH_SIGNAL_IGN(SIGUSR1);
- CATCH_SIGNAL_IGN(SIGUSR2);
- # ifdef SIGXCPU
- CATCH_SIGNAL_DFL(SIGXCPU);
- # endif
- # ifdef SIGDANGER
- CATCH_SIGNAL_DFL(SIGDANGER);
- # endif
- # undef CATCH_SIGNAL
-
- return;
-
- FAIL:
- #ifdef PROC2_DEBUG_PRINT
- sprintf(buf,"\tproc2 (%lu): error handling signal %d",
- (unsigned long) getpid(), sig);
- perror(buf);
- #endif /* PROC2_DEBUG_PRINT */
- exit(1);
- }
-
-
- static void dns_driver_init(int argc, char **argv, int in_fd, int out_fd);
- static void dns_driver_main_loop(int in_fd, int out_fd);
-
- /* Essentially, this is the main() of the forked dns-helper process.
- */
- static void
- dns_driver(int argc, char **argv, int in_fd, int out_fd)
- {
- /* Might not return, if we fork-and-exec. */
- dns_driver_init(argc, argv, in_fd, out_fd);
-
- /* Shouldn't return until proc1 has closed its end of the pipe. */
- dns_driver_main_loop(in_fd, out_fd);
- }
-
-
- static void
- dns_driver_init(int argc, char **argv, int in_fd, int out_fd)
- {
- #ifdef CHANGING_ARGV_WORKS
- {
- LOG_PROC2("launched","","\n");
- ERROR! Write me! (This should overwrite argv.)
- }
- #else /* !CHANGING_ARGV_WORKS */
- {
- char proc2_name[] = DNS_PROC2_NAME "\000";
-
- /* If argv[0] is already the proc2 name, then we're already in proc2,
- so there's nothing else to do.
-
- Otherwise, re-exec the running program with a different name.
- Kludge, kludge, kludge.
- */
- if (!strcmp(argv[0], proc2_name))
- {
- LOG_PROC2("dns_driver_init","not respawning.\n","");
- }
- else
- {
- char *new_argv[2];
- new_argv[0] = proc2_name;
- new_argv[1] = 0;
-
- LOG_PROC2("dns_driver_init",
- "execvp(... \"" DNS_PROC2_NAME "\")\n", "");
-
- /* Copy in_fd and out_fd onto stdin/stdout, in order to pass them
- to the newly-exec'ed process. */
- dup2(in_fd, fileno(stdin));
- dup2(out_fd, fileno(stdout));
-
- execvp(argv[0], new_argv);
-
- fprintf(stderr,
- "\nMozilla: execvp(\"%s\") failed!\n"
- "\tThis means that we were unable to fork() the dns-helper process,\n"
- "\tand so host-name lookups will happen in the foreground instead\n"
- "\tof in the background (and therefore won't be interruptible.)\n\n",
- argv[0]);
- exit(0);
- }
- }
- #endif /* !CHANGING_ARGV_WORKS */
-
-
- /* Close any streams we're not using.
- */
- if (fileno(stdin) != in_fd && fileno(stdin) != out_fd)
- fclose(stdin);
- if (fileno(stdout) != in_fd && fileno(stdout) != out_fd)
- fclose(stdout);
- /* Never close stderr -- proc2 needs it in case the execvp() fails. */
-
- dns_socks_kludge();
- dns_catch_signals();
-
- #ifdef RANDOMIZE_ADDRESSES
- {
- struct timeval tp;
- struct timezone tzp;
- int seed;
- gettimeofday(&tp, &tzp);
- seed = (999*tp.tv_sec) + (1001*tp.tv_usec) + (1003 * getpid());
- SRANDOM(seed);
- }
- #endif /* RANDOMIZE_ADDRESSES */
-
-
- #if defined(NSPR) || defined(JAVA)
- #ifndef NSPR20
- /* Hate hate hate hate!! */
- LOG_PROC2("dns_driver_init","calling PR_Init.\n","");
- PR_Init("dns_helper", 24, 1, 0);
- #endif
- #endif
- }
-
-
- static void
- dns_driver_main_loop(int in_fd, int out_fd)
- {
- int status = 0;
- FILE *in = fdopen(in_fd, "r");
- ASSERT(in);
- if (!in) return; /* out of memory *already*? We lost real bad... */
- SET_BUFFERING(in);
-
- while (1)
- {
- fd_set fdset;
- char line[MAXHOSTNAMELEN + 100];
- dns_lookup *obj = 0;
- dns_lookup *next = 0;
-
- FD_ZERO(&fdset);
-
- /* Select on our parent's input stream. */
- FD_SET(in_fd, &fdset);
-
- /* And select on the pipe associated with each child process. */
- if (proc2_queue)
- for (obj = proc2_queue; obj; obj = obj->next)
- {
- int fd;
- ASSERT(obj->fd);
- if (!obj->fd) continue;
- fd = fileno(obj->fd);
- ASSERT(fd >= 0 &&
- fd != in_fd &&
- fd != out_fd &&
- !FD_ISSET(fd, &fdset));
- FD_SET(fd, &fdset);
- }
-
- #ifdef PROC2_DEBUG_PRINT
- {
- int i = 0, j = 0;
- fprintf(stderr, "\tproc2 (%lu): entering select(",
- (unsigned long) getpid());
- for (i = 0; i < getdtablehi(); i++)
- if (FD_ISSET(i, &fdset))
- {
- fprintf(stderr, "%s%d", (j == 0 ? "" : ", "), i);
- j++;
- }
- fprintf(stderr, ")\n");
- }
- #endif /* PROC2_DEBUG_PRINT */
-
- status = select(getdtablehi(), &fdset, 0, 0, 0);
-
- LOG_PROC2("select returned","","\n");
-
- ASSERT(status > 0);
- if (status <= 0) continue;
-
-
- /* Handle input from our parent.
- */
-
- if (FD_ISSET (in_fd, &fdset))
- {
- /* Read one line from the parent (blocking if necessary, which
- it shouldn't be except for a trivial amount of time.) */
- *line = 0;
- fgets(line, sizeof(line)-1, in);
-
- if (!*line)
- {
- LOG_PROC2("got EOF from proc1; exiting","","\n");
- return;
- }
-
- LOG_PROC2("read command",line,"");
-
- string_trim(line);
-
- if (!strncmp(line, "kill: ", 6))
- {
- long id = 0;
- status = sscanf(line+6, "%ld\n", &id);
- ASSERT(status == 1);
- if (id)
- cancel_lookup(id, out_fd);
- }
- else if (!strncmp(line, "lookup: ", 8))
- {
- char *name = line + 8;
- obj = spawn_lookup_process(name, out_fd);
- /* Write a response to the parent immediately (to assign an id.)
- */
- if (obj)
- sprintf(line, "%d: %lu\n",
- DNS_STATUS_LOOKUP_STARTED,
- obj->id);
- else
- sprintf(line, "%d: %lu\n",
- DNS_STATUS_LOOKUP_NOT_STARTED,
- obj->id);
-
- LOG_PROC2("writing initial",line,"");
- write(out_fd, line, strlen(line));
- FLUSH(out_fd);
- }
- }
-
-
- /* Handle input from our kids.
- */
-
- for (obj = proc2_queue; obj; obj = next)
- {
- next=obj->next;
- if (FD_ISSET(fileno(obj->fd), &fdset))
- {
- pid_t pid = obj->pid;
- pid_t pid2;
-
- /* Read one line from the subprocess (blocking if necessary,
- which it shouldn't be except for a trivial amount of time.) */
- *line = 0;
- fgets(line, sizeof(line)-1, obj->fd);
-
- #ifdef PROC2_DEBUG_PRINT
- fprintf(stderr, "\tproc2 (%lu): read from proc3 (%ld): %s",
- (unsigned long) getpid(), obj->id, line);
- #endif /* PROC2_DEBUG_PRINT */
-
- handle_subproc_response(obj, line, out_fd);
-
- /* `obj' has now been freed. After having written one line, the
- process should exit. Wait for it now (we saved `pid' before
- `obj' got freed.)
-
- The thing that confuses me is, if I *don't* do the waitpid()
- here, zombies don't seem to get left around. I expected them
- to, and don't know why they don't. (On Irix 6.2, at least.)
- */
- pid2 = waitpid(pid, 0, 0);
- #ifndef NSPR20
- /*
- * nspr20 library catches the SIGCLD signal so that it can terminate the
- * application when an sproc terminates abnormally. Sometimes the signal
- * handler may be called before the call to waitpid above and in that case
- * waitpid will fail with a ECHILD error.
- */
- ASSERT(pid == pid2);
- #endif
- }
- }
- }
-
- ASSERT(0); /* not reached */
- }
-
-
-
- /* Process 1 (the user-called code.)
- */
-
-
- static FILE *dns_in_fd = 0;
- static FILE *dns_out_fd = 0;
-
-
- typedef struct queued_response {
- long id;
- int (*cb) (void *id, void *closure, int status, const char *result);
- void *closure;
- struct queued_response *next, *prev;
- } queued_response;
-
- static queued_response *proc1_queue = 0;
-
-
- static long
- proc1_queue_length(void)
- {
- queued_response *obj;
- long i = 0;
- for (obj = proc1_queue; obj; obj = obj->next) i++;
- return i;
- }
-
-
- /* Call this from main() to initialize the async DNS library.
- Returns a file descriptor that should be selected for, or
- negative if something went wrong. Pass it the argc/argv
- that your `main' was called with (it needs these pointers
- in order to give its forked subprocesses sensible names.)
- */
- int
- DNS_SpawnProcess(int argc, char **argv)
- {
- static int already_spawned_p = 0;
- pid_t forked;
- int infds[2]; /* "in" and "out" are from the p.o.v. of proc2. */
- int outfds[2]; /* one reads from [0] and writes to [1]. */
-
- ASSERT(!already_spawned_p);
- if (already_spawned_p) return -1;
- already_spawned_p = 1;
-
- #ifndef CHANGING_ARGV_WORKS
- if (!strcmp(argv[0], DNS_PROC2_NAME))
- {
- /* Oh look, we're already in proc2, so don't fork again.
- Assume our side of the pipe is on stdin/stdout.
- */
- LOG_PROC2("DNS_SpawnProcess","reconnecting to stdin/stdout.\n","");
-
- /* doesn't return until the other side closes the pipe. */
- dns_driver(argc, argv, fileno(stdin), fileno(stdout));
- exit(0);
- }
- #endif /* !CHANGING_ARGV_WORKS */
-
- LOG_PROC1("DNS_SpawnProcess","forking proc2.\n","");
-
- if (pipe (infds))
- {
- return -1; /* pipe error: return better error code? */
- }
- if (pipe (outfds))
- {
- close(infds[0]);
- close(infds[1]);
- return -1; /* pipe error: return better error code? */
- }
-
- switch (forked = fork ())
- {
- case -1:
- {
- close (infds[0]);
- close (infds[1]);
- close (outfds[0]);
- close (outfds[1]);
- return -1; /* fork error: return better error code? */
- }
- break;
-
- case 0: /* This is the forked process. */
- {
- /* Close the other sides of the pipes (used by the main fork.) */
- close (infds[1]);
- close (outfds[0]);
-
- /* doesn't return until the other side closes the pipe. */
- dns_driver(argc, argv, infds[0], outfds[1]);
-
- close (infds[0]);
- close (outfds[1]);
- exit(-1);
- }
- break;
-
- default:
- {
- /* This is the "old" process (subproc pid is in `forked'.)
- (Save that somewhere?)
- */
-
- /* This is the file descriptor we created for the benefit
- of the child process - we don't need it in the parent. */
- close (infds[0]);
- close (outfds[1]);
-
- /* Set up FILE objects for the proc3 side of the pipes.
- (Note that the words "out" in `dns_out_fd' and `outfds'
- have opposite senses here.)
- */
- dns_out_fd = fdopen(infds[1], "w");
- dns_in_fd = fdopen(outfds[0], "r");
-
- SET_BUFFERING(dns_out_fd);
- SET_BUFFERING(dns_in_fd);
-
- return fileno(dns_in_fd); /* ok! */
- }
- break;
- }
- }
-
-
- static queued_response *
- new_queued_response (int (*cb) (void *, void *closure, int, const char *),
- void *closure)
- {
- queued_response *obj;
-
- obj = (queued_response *) malloc(sizeof(*obj));
- if (!obj) return 0;
- memset(obj, 0, sizeof(*obj));
- obj->id = 0;
- obj->cb = cb;
- obj->closure = closure;
-
- ASSERT(!proc1_queue || proc1_queue->prev == 0);
- obj->next = proc1_queue;
- if (proc1_queue)
- proc1_queue->prev = obj;
- proc1_queue = obj;
-
- return obj;
- }
-
-
- /* Frees the object associated with a host lookup on which proc1 is waiting,
- and runs the callback function. The returned value is that of the callback.
- */
- static int
- free_queued_response (queued_response *obj, int status, const char *result)
- {
- long id = obj->id;
- int (*cb) (void *id, void *closure, int status, const char *result)
- = obj->cb;
- void *closure = obj->closure;
-
- if( -1 == id ) return 0;
-
- if (obj->prev)
- {
- ASSERT(obj->prev->next != obj->next);
- obj->prev->next = obj->next;
- }
- if (obj->next)
- {
- ASSERT(obj->next->prev != obj->prev);
- obj->next->prev = obj->prev;
- }
- if (proc1_queue == obj)
- proc1_queue = obj->next;
-
- ASSERT(closure != (void *) obj);
- memset(obj, ~0, sizeof(*obj));
- free(obj);
-
- ASSERT(status < -100 && status > -700);
- if (status == -DNS_STATUS_LOOKUP_OK) status = 1;
- else if (status == -DNS_STATUS_KILL_FAILED) status = 0;
-
- if (cb)
- return cb((void *) id, closure, status, result);
- else
- return 0;
- }
-
-
- /* Given a line which is an asynchronous response from proc2, find the
- associated lookup object, run the callback, return the status that
- the callback returned, and free the queued_response object.
- */
- static int
- handle_async_response(char *line)
- {
- int i;
- int code = 0;
- queued_response *obj = 0;
- long id = 0;
- int iip[4];
-
- ASSERT(line);
- if (!line || !*line) return -1;
-
- i = sscanf(line, "%d: %lu: %d.%d.%d.%d\n",
- &code, &id,
- &iip[0], &iip[1], &iip[2], &iip[3]);
- if (i != 6)
- {
- i = sscanf(line, "%d: %lu:", &code, &id);
- if (i != 2)
- {
- i = sscanf(line, "%d:", &code);
- if (i != 1)
- code = DNS_STATUS_INTERNAL_ERROR;
- }
- }
-
- if (id)
- for (obj = proc1_queue; obj; obj = obj->next)
- if (obj->id == id) break;
-
- if (!obj) /* got an id that we don't know about? */
- return -1;
-
- switch (code)
- {
- case DNS_STATUS_LOOKUP_OK: /* "1xx: id: a.b.c.d\n" */
- {
- unsigned char ip[5];
- ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3];
- ip[4] = 0; /* just in case */
- return free_queued_response(obj, -DNS_STATUS_LOOKUP_OK, (char *) ip);
- break;
- }
- case DNS_STATUS_LOOKUP_FAILED: /* "5xx: id: host not found\n" */
- default:
- {
- char *msg = 0;
- ASSERT(code == DNS_STATUS_LOOKUP_FAILED);
- msg = strchr(line, ':');
- if (msg)
- {
- char *s = strchr(msg, ':');
- if (s) msg = s+1;
- }
- else
- {
- msg = line;
- }
-
- code = (code > 100 ? -code : -DNS_STATUS_INTERNAL_ERROR);
- free_queued_response(obj, code, string_trim(msg));
- return code;
- break;
- }
- }
- ASSERT(0); /* not reached */
- }
-
-
-
- /* Kick off an async DNS lookup;
- The returned value is an id representing this transaction;
- the result_callback will be run (in the main process) when we
- have a result. Returns negative if something went wrong.
- If `status' is negative,`result' is an error message.
- If `status' is positive, `result' is a 4-character string of
- the IP address.
- If `status' is 0, then the lookup was prematurely aborted
- via a call to DNS_AbortHostLookup().
- */
- int
- DNS_AsyncLookupHost(const char *name,
- int (*result_callback) (void *id,
- void *closure,
- int status,
- const char *result),
- void *closure,
- void **id_return)
- {
- char buf[MAXHOSTNAMELEN + 100];
- queued_response *obj = 0;
- int code = 0;
- int i;
- char *s;
-
- ASSERT(result_callback);
- if (!result_callback) return -1;
- ASSERT(id_return);
- if (!id_return) return -1;
- *id_return = 0;
-
- ASSERT(name);
- if (!name || !*name) return -1; /* invalid host name: better error code? */
- if (strchr(name, '\n')) return -1; /* ditto */
- if (strchr(name, '\r')) return -1;
-
- if (strlen(name) > MAXHOSTNAMELEN)
- return -1; /* host name too long: return better error code? */
-
- obj = new_queued_response(result_callback, closure);
- if (!obj) return -1; /* out of memory: return better error code? */
-
-
- LOG_PROC1("writing: lookup",name,"\n");
- fprintf(dns_out_fd, "lookup: %s\n", name);
- fflush(dns_out_fd);
- FLUSH(fileno(dns_out_fd));
-
- /* We have just written a "lookup:" command to proc2.
- As soon as this command is received, proc2 will write a 2xx response
- back to us, ACKing it, and giving us an ID. We must wait for that
- response in order to return the ID to our caller; we block waiting
- for it, knowing that it won't be long in coming.
-
- However, there could be other data in the pipe already -- the final
- results of previously launched commands (1xx responses or 5xx responses.)
- So we read commands, and process them, until we find the one we're
- looking for.
-
- Under no circumstances can there be more than one 2xx response in the
- pipe at a time (since this is the only place a command is issued that
- can cause a 2xx response to be generated, and each call waits for it.)
- */
-
- AGAIN:
-
- *buf = 0;
- fgets(buf, sizeof(buf)-1, dns_in_fd);
- LOG_PROC1("read (lookup)",buf,"");
-
- obj->id = 0;
-
- if (!*buf) /* EOF, perhaps? */
- {
- code = DNS_STATUS_INTERNAL_ERROR;
- }
- else
- {
- i = sscanf(buf, "%d:", &code);
- ASSERT(i == 1);
- if (code <= 100) code = DNS_STATUS_INTERNAL_ERROR;
- }
-
- switch (code)
- {
- case DNS_STATUS_LOOKUP_STARTED: /* 2xx: id\n" */
- i = sscanf(buf, "%d: %lu\n", &code, &obj->id);
- ASSERT(i == 2);
- *id_return = (void *) obj->id;
- return 0;
- break;
-
- case DNS_STATUS_LOOKUP_OK: /* 1xx or 5xx which is an */
- case DNS_STATUS_LOOKUP_FAILED: /* async response (not for us) */
- handle_async_response(buf);
- goto AGAIN;
- break;
-
- case DNS_STATUS_LOOKUP_NOT_STARTED:
- default: /* 5xx: error msg\n"
- Presumably this is for us.
- (If not, there's a bug...) */
- ASSERT(code == DNS_STATUS_LOOKUP_NOT_STARTED);
- s = strchr(buf, ':');
- if (s) s++;
- code = (code > 100 ? -code : -DNS_STATUS_INTERNAL_ERROR);
- code = free_queued_response(obj, code, (s ? string_trim(s) : "???"));
- obj = 0;
- return code;
- break;
- }
- ASSERT(0); /* not reached */
- return -1;
- }
-
-
- /* Prematurely give up on the given host-lookup transaction.
- The `token' is what was returned by DNS_AsyncLookupHost.
- This causes the result_callback to be called with a negative
- status.
- */
- int
- DNS_AbortHostLookup(void *token)
- {
- char buf[MAXHOSTNAMELEN + 100];
- int code = 0;
- int i;
- long id = (int) token;
- long id2 = 0;
- queued_response *obj = 0;
- char *s;
-
- for (obj = proc1_queue; obj; obj = obj->next)
- if (obj->id == id) break;
-
- if (!obj) return -1;
-
- #ifdef PROC1_DEBUG_PRINT
- fprintf(stderr, "\tproc1 (%lu): writing: kill: %lu (ql=%ld)\n",
- (unsigned long) getpid(), id, proc1_queue_length());
- #endif /* PROC1_DEBUG_PRINT */
-
- fprintf(dns_out_fd, "kill: %lu\n", id);
- fflush(dns_out_fd);
- FLUSH(fileno(dns_out_fd));
-
-
- /* We have just written a "kill:" command to proc2.
- As soon as this command is received, proc2 will write a 1xx or 5xx
- response back to us, ACKing it. We must wait for that response in
- order to return an error code to our caller; we block waiting for it,
- knowing that it won't be long in coming.
-
- However, there could be other data in the pipe already -- the final
- results of previously launched commands (1xx responses or 5xx responses.)
- So we read commands, and process them, until we find the one we're
- looking for.
-
- Under no circumstances can there be more than one kill-response in the
- pipe at a time (since this is the only place a command is issued that
- can cause a kill-response response to be generated, and each call waits
- for it.)
- */
-
- AGAIN:
-
- *buf = 0;
- fgets(buf, sizeof(buf)-1, dns_in_fd);
- LOG_PROC1("read (kill)",buf,"");
-
- i = sscanf(buf, "%d:", &code);
- ASSERT(i == 1);
- if (code <= 100) code = DNS_STATUS_INTERNAL_ERROR;
-
- switch (code)
- {
- case DNS_STATUS_KILLED_OK:
- return free_queued_response(obj, -DNS_STATUS_KILLED_OK, "killed");
- break;
-
- case DNS_STATUS_LOOKUP_OK: /* 1xx or 5xx which is an */
- case DNS_STATUS_LOOKUP_FAILED: /* async response (not for us) */
-
- /* Actually, it might be for us -- if we've issued a kill, but the
- success/failure for this same code raced in before the response
- to the kill (a kill-failure, presumably) then ignore this response
- (wait for the kill-failure instead.) */
- id2 = 0;
- i = sscanf(buf, "%d: %lu:", &code, &id2);
- ASSERT(i == 2);
- if (i == 2 && id2 == id) {
- LOG_PROC1("Bug #87736 (1)",buf,"");
- handle_async_response(buf);
- goto AGAIN;
- }
-
- /* Not for us -- let it through. */
- handle_async_response(buf);
- goto AGAIN;
- break;
-
- case DNS_STATUS_KILL_FAILED:
- default: /* 5xx: error msg\n"
- Presumably this is for us.
- (If not, there's a bug...) */
- ASSERT(code == DNS_STATUS_KILL_FAILED);
- s = strchr(buf, ':');
- if (s)
- {
- char *s2 = strchr(++s, ':');
- if (s2) s = s2+1;
- }
- code = (code > 100 ? -code : -DNS_STATUS_INTERNAL_ERROR);
- free_queued_response(obj, code, (s ? string_trim(s) : "???"));
- LOG_PROC1("Bug #87736 (2)",buf,"");
- return code;
- break;
- }
- ASSERT(0); /* not reached */
- return -1;
- }
-
-
-
- /* The main select() loop of your program should call this when the fd
- that was returned by DNS_SpawnProcess comes active. This may cause
- some of the result_callback functions to run.
-
- If this returns negative, then a fatal error has happened, and you
- should close `fd' and not select() for it again. Call gethostbyname()
- in the foreground or something.
- */
- int
- DNS_ServiceProcess(int fd)
- {
- char buf[MAXHOSTNAMELEN + 100];
- char *s = 0;
- int status = 0;
- int final_status = 0;
-
- AGAIN:
-
- ASSERT(fd == fileno(dns_in_fd));
- if (fd != fileno(dns_in_fd)) return -1;
-
- ASSERT(final_status <= 0);
-
- /* Poll to see if input is available on fd.
- NOTE: This only works because we've marked the FILE* object wrapped
- around this fd as being fully unbuffered!!
- */
- {
- fd_set fdset;
- struct timeval t = { 0L, 0L, };
- FD_ZERO(&fdset);
- FD_SET(fd, &fdset);
- status = select(fd+1, &fdset, 0, 0, &t);
- if (status < 0 && final_status == 0)
- final_status = status;
- if (status <= 0) /* error, or no input ready */
- return final_status;
- }
-
- *buf = 0;
- s = fgets(buf, sizeof(buf)-1, dns_in_fd);
-
- if (s && *s)
- {
- LOG_PROC1("read (async)",buf,"");
- }
- else
- {
- /* #### If there's nothing to read, the socket must be closed? */
- LOG_PROC1("read (async)","nothing -- returning error\n","");
- return (final_status < 0 ? final_status : -1);
- }
-
- /* call all pending procs, but remember the first error code that
- one of them returned.
- */
- status = handle_async_response(buf);
- status = 0; /* #### */
- if (status < 0 && final_status == 0)
- final_status = status;
-
- /* see if there is more to read. */
- goto AGAIN;
- }
-
-
-
- #ifdef DEBUG
- void
- print_fdset(fd_set *fdset)
- {
- int i;
- int any = 0;
- if (!fdset) return;
- for (i = 0; i < getdtablesize(); i++)
- {
- if (FD_ISSET(i, fdset))
- {
- if (any) fprintf(stderr, ", ");
- any = 1;
- fprintf(stderr, "%d", i);
- }
- }
- }
-
- void
- print_fd(int fd)
- {
- int status;
- fd_set fdset;
- struct timeval t = { 0L, 0L, };
-
- fprintf(stderr, " %d:\n", fd);
-
- FD_ZERO(&fdset);
- FD_SET(fd, &fdset);
- status = select(fd+1, &fdset, 0, 0, &t);
- if (status == 0)
- fprintf(stderr, " readable (no input)\n");
- else if (status == 1)
- fprintf(stderr, " readable (pending)\n");
- else
- fprintf(stderr, " unreadable (error %d)\n", status);
-
- FD_ZERO(&fdset);
- FD_SET(fd, &fdset);
- status = select(fd+1, 0, &fdset, 0, &t);
- if (status == 0)
- fprintf(stderr, " writeable (no input)\n");
- else if (status == 1)
- fprintf(stderr, " writeable (pending)\n");
- else
- fprintf(stderr, " unwriteable (error %d)\n", status);
-
- FD_ZERO(&fdset);
- FD_SET(fd, &fdset);
- status = select(fd+1, 0, 0, &fdset, &t);
- if (status == 0)
- fprintf(stderr, " exceptionable (no input)\n");
- else if (status == 1)
- fprintf(stderr, " exceptionable (pending)\n");
- else
- fprintf(stderr, " unexceptionable (error %d)\n", status);
- }
-
-
- void
- print_fdsets(fd_set *read, fd_set *write, fd_set *except)
- {
- int i;
- fd_set all;
- FD_ZERO(&all);
-
- fprintf(stderr, "rd: "); print_fdset(read);
- fprintf(stderr, "\nwr: "); print_fdset(write);
- fprintf(stderr, "\nex: "); print_fdset(except);
- fprintf(stderr, "\n");
-
- if (read)
- for (i = 0; i < getdtablesize(); i++)
- if (FD_ISSET(i, read)) FD_SET(i, &all);
- if (write)
- for (i = 0; i < getdtablesize(); i++)
- if (FD_ISSET(i, write)) FD_SET(i, &all);
- if (except)
- for (i = 0; i < getdtablesize(); i++)
- if (FD_ISSET(i, except)) FD_SET(i, &all);
-
- for (i = 0; i < getdtablesize(); i++)
- if (FD_ISSET(i, &all)) print_fd(i);
- }
-
- #endif /* DEBUG */
-
-
-
-
- #ifdef STANDALONE
-
- typedef struct test_id_cons {
- char *name;
- void *id;
- struct test_id_cons *next;
- } test_id_cons;
- static test_id_cons *test_list = 0;
-
- static int
- test_cb (void *id, void *closure, int status, const char *result)
- {
- const char *argv0 = (char *) closure;
- test_id_cons *cons = test_list;
-
- while(cons)
- {
- if (cons->id == id) break;
- cons = cons->next;
- ASSERT(cons);
- }
-
- if (status == 1)
- {
- unsigned char *ip = (unsigned char *) result;
- fprintf(stderr, "%s: %s = %d.%d.%d.%d\n",
- argv0, cons->name, ip[0], ip[1], ip[2], ip[3]);
- }
- else
- {
- fprintf(stderr, "%s: %s: error %d: %s\n",
- argv0, cons->name, status, result);
- }
- return 0;
- }
-
- int
- main (int argc, char **argv)
- {
- char buf[MAXHOSTNAMELEN + 100];
- int fd = DNS_SpawnProcess(argc, argv);
-
- if (fd <= 0)
- {
- fprintf(stderr, "%s: dns init failed\n", argv[0]);
- exit(-1);
- }
-
- fprintf(stderr,
- "Type host names to spawn lookups.\n"
- "Type `abort ID' to kill one off.\n"
- "Type `quit' when done.\n\n");
-
- while(1)
- {
- int status = 0;
- fd_set fdset;
-
- FD_ZERO(&fdset);
- FD_SET(fd, &fdset);
- FD_SET(fileno(stdin), &fdset);
-
- status = select(getdtablehi(), &fdset, 0, 0, 0);
- if (status <= 0)
- {
- fprintf(stderr, "%s: select() returned %d\n", argv[0], status);
- exit(-1);
- }
-
- if (!FD_ISSET(fd, &fdset) &&
- !FD_ISSET(fileno(stdin), &fdset))
- {
- fprintf(stderr, "%s: neither fd is set?\n", argv[0]);
- exit(-1);
- }
-
- if (FD_ISSET(fd, &fdset))
- {
- status = DNS_ServiceProcess(fd);
- if (status < 0)
- {
- fprintf(stderr, "%s: DNS_ServiceProcess() returned %d\n",
- argv[0], status);
- exit(-1);
- }
- }
-
- if (FD_ISSET(fileno(stdin), &fdset))
- {
- /* Read a line from the user. */
- char *line = fgets(buf, sizeof(buf)-1, stdin);
-
- line = string_trim(line);
-
- if (!strcmp(line, "quit") || !strcmp(line, "exit"))
- {
- fprintf(stderr, "buh bye now\n");
- exit(0);
- }
- else if (!strncmp(line, "abort ", 6))
- {
- long id;
- status = sscanf(line+6, "%lu\n", &id);
- if (status != 1)
- {
- test_id_cons *cons = test_list;
- while(cons)
- {
- if (!strcmp(cons->name, line+6)) break;
- cons = cons->next;
- }
- if (cons)
- id = (long) cons->id;
- else
- {
- fprintf(stderr, "%s: %s is not a known host or id.\n",
- argv[0], line+6);
- continue;
- }
- }
-
- status = DNS_AbortHostLookup((void *)id);
- if (status < 0)
- fprintf(stderr, "%s: DNS_AbortHostLookup(%ld) returned %d\n",
- argv[0], id, status);
- }
- else if (strchr(line, ' ') || strchr(line, '\t'))
- {
- fprintf(stderr, "%s: unrecognized command %s.\n",
- argv[0], line);
- }
- else
- {
- void *id = 0;
- test_id_cons *cons = 0;
-
- fprintf(stderr, "%s: looking up %s...", argv[0], line);
- status = DNS_AsyncLookupHost(line, test_cb, argv[0], &id);
- if (status == 0)
- fprintf(stderr, " id = %lu.\n", (long)id);
- else
- fprintf(stderr,
- "\n%s: DNS_AsyncLookupHost(%s) returned %d (id = %lu)\n",
- argv[0], line, status, (long)id);
-
- cons = (test_id_cons *) malloc(sizeof(*cons));
- cons->name = strdup(line);
- cons->id = id;
- cons->next = test_list;
- test_list = cons;
- }
- }
- }
- }
-
- #endif /* STANDALONE */
- #endif /* UNIX_ASYNC_DNS && XP_UNIX */
-