home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libnet / unix-dns.c < prev   
Encoding:
C/C++ Source or Header  |  1998-04-08  |  45.5 KB  |  1,777 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.  * unix-dns.c --- portable nonblocking DNS for Unix
  21.  * Created: Jamie Zawinski <jwz@netscape.com>, 19-Dec-96.
  22.  */
  23.  
  24. #if defined(XP_UNIX) && defined(UNIX_ASYNC_DNS)
  25.  
  26. static void dns_socks_kludge(void);
  27.  
  28. /* Todo:
  29.  
  30.    = Should we actually use the CHANGING_ARGV_WORKS code, on systems where
  31.      that is true, or just always do the exec() hack?  If we use it, it
  32.      needs to be debugged more, and the exact set of systems on which it
  33.      works needs to be determined.  (Works on: SunOS 4, Linux, AIX, OSF;
  34.      doesn't work on: Solaris, Irix, UnixWare, HPUX.)  (But HPUX has an
  35.      undocumented ioctl that lets you change a proc's name.)   (Unix sucks
  36.      a lot.)
  37.  */
  38.  
  39.  
  40. /* Compile-time options:
  41.    -DDNS_EXPLICITLY_FLUSH_PIPES
  42.        to do ioctls() to flush out the pipes after each write().
  43.     at one point I thought I needed this, but it doesn't
  44.     actually seem to be necessary.
  45.  
  46.    -DGETHOSTBYNAME_DELAY=N
  47.        to insert an artificial delay of N seconds before each
  48.     call to gethostbyname (in order to simulate DNS lossage.)
  49.  
  50.    -DNO_SOCKS_NS_KLUDGE
  51.        Set this to *disable* the $SOCKS_NS kludge.  Otherwise,
  52.     that environment variable will be consulted for use as an
  53.     alternate DNS root.  It's historical; don't ask me...
  54.  
  55.    -DRANDOMIZE_ADDRESSES
  56.     This code only deals with one IP address per host; if this
  57.     is set, then that host will be randomly selected from among
  58.     all the addresses of the host; otherwise, it will be the
  59.     first address.
  60.  
  61.    -DCHANGING_ARGV_WORKS
  62.         (This option isn't fully implemented yet, and can't work
  63.     on some systems anyway.)
  64.  
  65.     The code in this file wants to fork(), and then change the
  66.     name of the second process as reported by `ps'.  Some systems
  67.     let you do that by overwriting argv[0], but some do not.
  68.  
  69.     If this isn't defined, we effect this change by re-execing
  70.     after the fork (since you can pass a new name that way.)
  71.     This depends on exec(argv[0] ... ) working, which it might
  72.     not if argv[0] has a non-absolute path in it, and the user
  73.     has played stupid games with $PATH or something.  Those
  74.     users probably deserve to lose.
  75.  
  76.    -DSTANDALONE
  77.        to include a main() for interactive command-line testing.
  78.  
  79.    -DPROC3_DEBUG_PRINT
  80.     to cause proc3 (the gethostbyname() processes) to print
  81.     diagnostics to stderr.
  82.  
  83.    -DPROC2_DEBUG_PRINT
  84.     to cause proc2 (the looping, dispatching process) to print
  85.     diagnostics to stderr.
  86.  
  87.    -DPROC1_DEBUG_PRINT
  88.     to cause proc1 (the main thread) to print diagnostics to
  89.     stderr.  */
  90.  
  91.  
  92. #include "unix-dns.h"
  93.  
  94. #include <stdlib.h>
  95. #include <unistd.h>
  96. #include <string.h>
  97. #include <stdio.h>
  98. #include <assert.h>
  99. #include <netdb.h>        /* for gethostbyname() */
  100. #include <signal.h>        /* for kill() */
  101. #include <sys/param.h>        /* for MAXHOSTNAMELEN */
  102. #include <sys/wait.h>        /* for waitpid() */
  103. #include <sys/time.h>        /* for gettimeofday() */
  104. #include <signal.h>        /* for signal() and the signal names */
  105.  
  106. #ifdef AIX
  107. #include <sys/select.h>  /* for fd_set */
  108. #endif
  109.  
  110. #ifndef NO_SOCKS_NS_KLUDGE
  111. # include <netinet/in.h>    /* for sockaddr_in (from inet.h) */
  112. # include <arpa/inet.h>        /* for in_addr (from nameser.h) */
  113. # include <arpa/nameser.h>    /* for MAXDNAME (from resolv.h) */
  114. # include <resolv.h>        /* for res_init() and _res */
  115. #endif
  116.  
  117. #if !defined(__irix)
  118.   /* Looks like Irix is the only one that has getdtablehi()? */
  119. # define getdtablehi() getdtablesize()
  120. #endif
  121.  
  122. #if !defined(__irix)
  123.   /* Looks like Irix and Solaris 5.5 are the only ones that have
  124.      getdtablesize()... but since Solaris 5.4 doesn't have it,
  125.      Solaris is out.  (If you find a system that doesn't even
  126.      have FD_SETSIZE, just grab your ankles and set it to 255.)
  127.    */
  128. # define getdtablesize() (FD_SETSIZE)
  129. #endif
  130.  
  131.  
  132. #ifdef DNS_EXPLICITLY_FLUSH_PIPES
  133. # include <stropts.h>        /* for I_FLUSH and FLUSHRW */
  134. #endif
  135.  
  136. /* Rumor has it that some systems lie about this value, and actually accept
  137.    host names longer than sys/param.h claims.  But it's definitely the case
  138.    that some systems (and again I don't know which) crash if you pass a
  139.    too-long hostname into gethostbyname().  So better safe than sorry.
  140.  */
  141. #ifndef MAXHOSTNAMELEN
  142. # define MAXHOSTNAMELEN 64
  143. #endif
  144.  
  145. #define ASSERT(x) assert(x)
  146.  
  147. #ifdef DNS_EXPLICITLY_FLUSH_PIPES
  148. # define FLUSH(fd) ioctl(fd, I_FLUSH, FLUSHRW)
  149. #else
  150. # define FLUSH(x)
  151. #endif
  152.  
  153. /* We use fdopen() to get the more convenient stdio interface on the pipes
  154.    we use; but we don't want the stdio library to provide *any* buffering;
  155.    just use the buffering that the pipe itself provides, so that when
  156.    select() says there is no input available, fgetc() agrees.
  157.  */
  158. #define SET_BUFFERING(file) setvbuf((file), NULL, _IONBF, 0)
  159.  
  160.  
  161. #ifdef RANDOMIZE_ADDRESSES
  162.   /* Unix is Random. */
  163. # if defined(UNIXWARE) || defined(_INCLUDE_HPUX_SOURCE) || (defined(__sun) && defined(__svr4__))
  164. #  define RANDOM  rand
  165. #  define SRANDOM srand
  166. # else /* !BSD-incompatible-Unixes */
  167. #  define RANDOM  random
  168. #  define SRANDOM srandom
  169. # endif /* !BSD-incompatible-Unixes */
  170. #endif /* RANDOMIZE_ADDRESSES */
  171.  
  172.  
  173. /* Way kludgy debugging/logging interface, since gdb's support
  174.    for debugging fork'ed processes is pathetic.
  175.  */
  176. #define LOG_PROCn(PROC,PREFIX,BUF,SUFFIX,QL) do{\
  177.     fprintf(stderr, \
  178.         "\t" PROC " (%lu): " PREFIX ": (ql=%ld) %s" SUFFIX, \
  179.         ((unsigned long) getpid()), QL, BUF); \
  180.   } while(0)
  181.  
  182. #ifdef PROC3_DEBUG_PRINT
  183. # define LOG_PROC3(PREFIX,BUF,SUFFIX) LOG_PROCn("proc3",PREFIX,BUF,SUFFIX,0L)
  184. #else
  185. # define LOG_PROC3(PREFIX,BUF,SUFFIX)
  186. #endif
  187.  
  188. #ifdef PROC2_DEBUG_PRINT
  189. # define LOG_PROC2(PREFIX,BUF,SUFFIX) LOG_PROCn("proc2",PREFIX,BUF,SUFFIX,\
  190.                         proc2_queue_length())
  191. #else
  192. # define LOG_PROC2(PREFIX,BUF,SUFFIX)
  193. #endif
  194.  
  195. #ifdef PROC1_DEBUG_PRINT
  196. # define LOG_PROC1(PREFIX,BUF,SUFFIX) LOG_PROCn("proc1",PREFIX,BUF,SUFFIX,\
  197.                         proc1_queue_length())
  198. #else
  199. # define LOG_PROC1(PREFIX,BUF,SUFFIX)
  200. #endif
  201.  
  202.  
  203. /* The internal status codes that are used; these follow the basic
  204.    SMTP/NNTP model of three-digit codes.
  205.  */
  206. #define DNS_STATUS_GETHOSTBYNAME_OK    101    /* proc3 -> proc2 */
  207. #define DNS_STATUS_LOOKUP_OK        102    /* proc2 -> proc1 */
  208. #define DNS_STATUS_KILLED_OK        103    /* proc2 -> proc1 */
  209. #define DNS_STATUS_LOOKUP_STARTED    201    /* proc2 -> proc1 */
  210. #define DNS_STATUS_GETHOSTBYNAME_FAILED 501    /* proc3 -> proc2 */
  211. #define DNS_STATUS_LOOKUP_FAILED    502    /* proc2 -> proc1 */
  212. #define DNS_STATUS_LOOKUP_NOT_STARTED    503    /* proc2 -> proc1 */
  213. #define DNS_STATUS_KILL_FAILED        504    /* proc2 -> proc1 */
  214. #define DNS_STATUS_UNIMPLEMENTED    601    /* unimplemented (ha ha) */
  215. #define DNS_STATUS_INTERNAL_ERROR    602    /* assertion failure */
  216.  
  217. #define DNS_PROC2_NAME "(dns helper)"
  218.  
  219.  
  220. static char *
  221. string_trim(char *s)
  222. {
  223.   char *s2;
  224.   if (!s) return 0;
  225.   s2 = s + strlen(s) - 1;
  226.   while (s2 > s && (*s2 == '\n' || *s2 == '\r' || *s2 == ' ' || *s2 == '\t'))
  227.     *s2-- = 0;
  228.   while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
  229.     s++;
  230.   return s;
  231. }
  232.  
  233.  
  234. /* Process 3.
  235.    Inherits a string (hostname) from the spawner.
  236.    Writes "1xx: a.b.c.d\n" or "5xx: error msg\n".
  237.  */
  238.  
  239. static void
  240. blocking_gethostbyname (const char *name, int out_fd,
  241.             unsigned long random_number)
  242. {
  243.   char buf[MAXHOSTNAMELEN + 100];
  244.   struct hostent *h;
  245.   unsigned long which_addr = 0;
  246.  
  247.   static int firstTime=1;
  248.   if (firstTime) {
  249.     firstTime=0;
  250.     dns_socks_kludge();
  251.   }
  252.  
  253. #if GETHOSTBYNAME_DELAY
  254.   {
  255.     int i = GETHOSTBYNAME_DELAY;
  256.     LOG_PROC3("sleeping","","\n");
  257.     sleep(i);
  258.   }
  259. #endif /* GETHOSTBYNAME_DELAY */
  260.  
  261.   LOG_PROC3("gethostbyname",name,"\n");
  262.  
  263.   h = gethostbyname(name);
  264.  
  265. #ifdef RANDOMIZE_ADDRESSES
  266.   if (h)
  267.     {
  268.       unsigned long n_addrs;
  269.       for (n_addrs = 0; h->h_addr_list[n_addrs]; n_addrs++)
  270.     ;
  271.       if (n_addrs > 0)
  272.     which_addr = (random_number % n_addrs);
  273.       else
  274.     h = 0;
  275.     }
  276. #endif /* RANDOMIZE_ADDRESSES */
  277.  
  278.   if (h)
  279.     sprintf(buf, "%d: %d.%d.%d.%d\n",
  280.         DNS_STATUS_GETHOSTBYNAME_OK,
  281.         ((unsigned char *) h->h_addr_list[which_addr])[0],
  282.         ((unsigned char *) h->h_addr_list[which_addr])[1],
  283.         ((unsigned char *) h->h_addr_list[which_addr])[2],
  284.         ((unsigned char *) h->h_addr_list[which_addr])[3]);
  285.   else
  286.     sprintf(buf, "%d: host %s not found\n",
  287.         DNS_STATUS_GETHOSTBYNAME_FAILED, name);
  288.  
  289.   LOG_PROC3("writing response",buf,"");
  290.  
  291.   write(out_fd, buf, strlen(buf));
  292.   FLUSH(out_fd);
  293. }
  294.  
  295.  
  296.  
  297. /* Process 2.
  298.    Loops forever.
  299.  
  300.    Reads "lookup: hostname\n".
  301.    Writes "2xx: id\n" or "5xx: error msg\n".
  302.    Some time later, writes "1xx: id: a.b.c.d\n" or "5xx: id: error msg\n".
  303.  
  304.    -or-
  305.  
  306.    Reads "kill: id\n".
  307.    Writes "1xx: killed" or "5xx: kill failed"
  308.  */
  309.  
  310. typedef struct dns_lookup {
  311.   long id;
  312.   pid_t pid;
  313.   FILE *fd;
  314.   char *name;
  315.   struct dns_lookup *next, *prev;
  316. } dns_lookup;
  317.  
  318. static long dns_id_tick;
  319. static dns_lookup *proc2_queue = 0;
  320.  
  321.  
  322. static long
  323. proc2_queue_length(void)
  324. {
  325.   dns_lookup *obj;
  326.   long i = 0;
  327.   for (obj = proc2_queue; obj; obj = obj->next) i++;
  328.   return i;
  329. }
  330.  
  331.  
  332. static dns_lookup *
  333. new_lookup_object (const char *name)
  334. {
  335.   dns_lookup *obj;
  336.   char *n2;
  337.   ASSERT(name);
  338.   if (!name) return 0;
  339.  
  340.   n2 = strdup(name);
  341.   if (!n2) return 0; /* MK_OUT_OF_MEMORY */
  342.  
  343.   obj = (dns_lookup *) malloc(sizeof(*obj));
  344.   if (!obj) return 0;
  345.   memset(obj, 0, sizeof(*obj));
  346.   obj->id = ++dns_id_tick;
  347.   obj->name = n2;
  348.   obj->fd = 0;
  349.  
  350.   ASSERT(!proc2_queue || proc2_queue->prev == 0);
  351.   obj->next = proc2_queue;
  352.   if (proc2_queue)
  353.     proc2_queue->prev = obj;
  354.   proc2_queue = obj;
  355.  
  356.   return obj;
  357. }
  358.  
  359.  
  360. /* Frees the object associated with a host lookup on which proc2 is waiting,
  361.    and writes the result to proc1.  Returns 0 if everything went ok, negative
  362.    otherwise.  Status should be 0 (for "ok") or negative (the inverse of one
  363.    of the three-digit response codes that *proc1* is expecting (not the codes
  364.    that *proc3* returns.)n
  365.  */
  366. static int
  367. free_lookup_object (dns_lookup *obj,
  368.             int out_fd,
  369.             int status, const char *error_msg,
  370.             const unsigned char ip_addr[4])
  371. {
  372.   char buf[MAXHOSTNAMELEN + 100];
  373.  
  374.   ASSERT(status == 0 || (status < -100 && status > -700));
  375.  
  376.   ASSERT(obj);
  377.   if (!obj) return -1;
  378.  
  379.   if (status >= 0 && ip_addr)
  380.     {
  381.       sprintf(buf, "%d: %lu: %d.%d.%d.%d\n",
  382.           DNS_STATUS_LOOKUP_OK,
  383.           obj->id, ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
  384.     }
  385.   else
  386.     {
  387.       ASSERT(error_msg);
  388.       sprintf(buf, "%d: %lu: %s\n",
  389.           ((status < -100) ? -status : DNS_STATUS_INTERNAL_ERROR),
  390.           obj->id, (error_msg ? error_msg : "???"));
  391.     }
  392.  
  393.   LOG_PROC2("writing response",buf,"");
  394.  
  395.   write(out_fd, buf, strlen(buf));
  396.   FLUSH(out_fd);
  397.  
  398.   if (obj->fd)
  399.     {
  400.       fclose(obj->fd);
  401.       obj->fd = 0;
  402.     }
  403.   if (obj->prev)
  404.     {
  405.       ASSERT(obj->prev->next != obj->next);
  406.       obj->prev->next = obj->next;
  407.     }
  408.   if (obj->next)
  409.     {
  410.       ASSERT(obj->next->prev != obj->prev);
  411.       obj->next->prev = obj->prev;
  412.     }
  413.   if (proc2_queue == obj)
  414.     proc2_queue = obj->next;
  415.  
  416.   memset(obj, ~0, sizeof(obj));
  417.   free(obj);
  418.  
  419.   return 0;
  420. }
  421.  
  422.  
  423. static dns_lookup *
  424. spawn_lookup_process (const char *name, int out_fd)
  425. {
  426.   dns_lookup *obj = new_lookup_object(name);
  427.   pid_t forked;
  428.   int fds[2];        /* one reads from [0] and writes to [1]. */
  429.   unsigned long random_number = 0;
  430.  
  431.   if (pipe (fds))
  432.     {
  433.       free_lookup_object(obj, out_fd,
  434.              -DNS_STATUS_LOOKUP_NOT_STARTED,
  435.              "can't make pipe", 0);
  436.       obj = 0;
  437.       return 0;
  438.     }
  439.  
  440.   /* Save the fd from which we should read from the forked proc. */
  441.   obj->fd = fdopen(fds[0], "r");
  442.   if (!obj->fd)
  443.     {
  444.       close(fds[0]);
  445.       close(fds[1]);
  446.       free_lookup_object(obj, out_fd,
  447.              -DNS_STATUS_LOOKUP_NOT_STARTED,
  448.              "out of memory", 0);
  449.       obj = 0;
  450.       return 0;
  451.     }
  452.   SET_BUFFERING(obj->fd);
  453.  
  454. #ifdef RANDOMIZE_ADDRESSES
  455.   /* Generate the random numbers in proc2 for use by proc3, so that the
  456.      PRNG actually gets permuted. */
  457.   random_number = (unsigned long) RANDOM();
  458. #endif /* RANDOMIZE_ADDRESSES */
  459.  
  460.  
  461.   switch (forked = fork ())
  462.     {
  463.     case -1:
  464.       {
  465.     close (fds[1]);
  466.     free_lookup_object(obj, out_fd,
  467.                -DNS_STATUS_LOOKUP_NOT_STARTED,
  468.                "can't fork", 0);
  469.     obj = 0;
  470.     return 0;
  471.       }
  472.     break;
  473.  
  474.     case 0:    /* This is the forked process. */
  475.       {
  476.     /* Close the other side of the pipe (it's used by the main fork.) */
  477.     close (fds[0]);
  478.  
  479.     /* Call gethostbyname, then write the result down the pipe. */
  480.     blocking_gethostbyname(name, fds[1], random_number);
  481.  
  482.     /* Now exit this process. */
  483.     close (fds[1]);
  484.     exit(0);
  485.       }
  486.     break;
  487.  
  488.     default:
  489.       {
  490.     /* This is the "old" process (subproc pid is in `forked'.) */
  491.     obj->pid = forked;
  492.  
  493.     /* This is the file descriptor we created for the benefit
  494.        of the child process - we don't need it in the parent. */
  495.     close (fds[1]);
  496.  
  497.     return obj;  /* ok. */
  498.       }
  499.     break;
  500.     }
  501.  
  502.   /* shouldn't get here. */
  503.   ASSERT(0);
  504.   return 0;
  505. }
  506.  
  507.  
  508. /* Given a line which is an asynchronous response from proc3, find the
  509.    associated lookup object, feed the response on to proc1, and free
  510.    the dns_lookup object.
  511.  */
  512. static void
  513. handle_subproc_response(dns_lookup *obj, const char *line, int out_fd)
  514. {
  515.   int iip[4];
  516.   char *s;
  517.   int code = 0;
  518.   int i;
  519.  
  520.   i = sscanf(line, "%d: %d.%d.%d.%d\n",
  521.          &code, &iip[0], &iip[1], &iip[2], &iip[3]);
  522.   if (i != 5)
  523.     {
  524.       i = sscanf(line, "%d:", &code);
  525.       if (i != 1 || code <= 100)
  526.     code = DNS_STATUS_INTERNAL_ERROR;
  527.     }
  528.  
  529.   switch (code)
  530.     {
  531.     case DNS_STATUS_GETHOSTBYNAME_OK:
  532.       {
  533.     unsigned char ip[5];
  534.     ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3];
  535.     ip[4] = 0;  /* just in case */
  536.     free_lookup_object (obj, out_fd, 0, 0, ip);
  537.     obj = 0;
  538.     break;
  539.       }
  540.  
  541.     case DNS_STATUS_GETHOSTBYNAME_FAILED:
  542.     default:
  543.       {
  544.     ASSERT(code == DNS_STATUS_GETHOSTBYNAME_FAILED);
  545.     if (code == DNS_STATUS_GETHOSTBYNAME_FAILED)
  546.       code = DNS_STATUS_LOOKUP_FAILED;
  547.     s = strchr(line, ':');
  548.     if (s) s++;
  549.     free_lookup_object (obj, out_fd,
  550.                 (code > 100
  551.                  ? -code
  552.                  : -DNS_STATUS_INTERNAL_ERROR),
  553.                 (s ? string_trim(s) : "???"), 0);
  554.     obj = 0;
  555.     break;
  556.       }
  557.     }
  558. }
  559.  
  560.  
  561. static void
  562. cancel_lookup(long id, int out_fd)
  563. {
  564.   dns_lookup *obj = 0;
  565.  
  566.   if (proc2_queue)
  567.     for (obj = proc2_queue; obj; obj = obj->next)
  568.       if (obj->id == id) break;
  569.  
  570.   if (obj)
  571.     {
  572.       if (obj->pid)
  573.     {
  574.       pid_t pid2;
  575.  
  576.       /*
  577.        * SIGKILL causes the browser to hang if the user clicks on the stop
  578.        * button while a long/bogus dns lookup is in progress.  According
  579.        * to signal guru asharma, we should use SIGQUIT instead. -re
  580.        */
  581. /*       kill(obj->pid, SIGKILL); */
  582.       kill(obj->pid, SIGQUIT);
  583.       pid2 = waitpid(obj->pid, 0, 0);
  584.       ASSERT(obj->pid == pid2);
  585.     }
  586.       free_lookup_object(obj, out_fd, -DNS_STATUS_KILLED_OK, "cancelled", 0);
  587.       obj = 0;
  588.     }
  589.   else
  590.     {
  591.       char buf[200];
  592.       sprintf (buf, "%d: %lu: unable to cancel\n", DNS_STATUS_KILL_FAILED, id);
  593.       LOG_PROC2("writing (kill) response",buf,"");
  594.       write(out_fd, buf, strlen(buf));
  595.     }
  596.  
  597.   FLUSH(out_fd);
  598. }
  599.  
  600.  
  601. static void
  602. dns_socks_kludge(void)
  603. {
  604. #ifndef NO_SOCKS_NS_KLUDGE
  605.   /* Gross historical kludge.
  606.      If the environment variable $SOCKS_NS is defined, stomp on the host that
  607.      the DNS code uses for host lookup to be a specific ip address.
  608.    */
  609.   char *ns = getenv("SOCKS_NS");
  610.   if (ns && *ns) 
  611.     {
  612.       /* Gross hack added to Gross historical kludge - need to
  613.        * initialize resolv.h structure first with low-cost call
  614.        * to gethostbyname() before hacking _res. Subsequent call
  615.        * to gethostbyname() will then pick up $SOCKS_NS address.
  616.        */
  617.       gethostbyname("localhost");
  618.  
  619.       res_init();
  620.       _res.nsaddr_list[0].sin_addr.s_addr = inet_addr(ns);
  621.       _res.nscount = 1;
  622.     }
  623. #endif /* !NO_SOCKS_NS_KLUDGE */
  624. }
  625.  
  626.  
  627. #ifdef PROC2_DEBUG_PRINT
  628. static void
  629. dns_signal_handler_dfl (int sig)
  630. {
  631.   char buf[100];
  632.   signal (sig, SIG_DFL);
  633.   sprintf(buf,"caught signal %d!\n",sig);
  634.   LOG_PROC2("dns_signal_handler",buf,"");
  635.   kill (getpid(), sig);
  636. }
  637. #endif /* PROC2_DEBUG_PRINT */
  638.  
  639. static void
  640. dns_signal_handler_ign (int sig)
  641. {
  642. #ifdef PROC2_DEBUG_PRINT
  643.   char buf[100];
  644.   sprintf(buf,"caught signal %d -- ignoring.\n",sig);
  645.   LOG_PROC2("dns_signal_handler",buf,"");
  646. #endif /* PROC2_DEBUG_PRINT */
  647.   signal (sig, dns_signal_handler_ign);
  648. }
  649.  
  650.  
  651. static void
  652. dns_catch_signals(void)
  653. {
  654. #ifndef SIG_ERR
  655. # define SIG_ERR -1
  656. #endif
  657.  
  658. #ifdef PROC2_DEBUG_PRINT
  659.   int sig;
  660.   char buf[255];
  661.   LOG_PROC2("dns_catch_signals","plugging in...\n","");
  662. # define CATCH_SIGNAL_DFL(SIG) sig = SIG; \
  663.   if (((int)signal(SIG, dns_signal_handler_dfl)) == ((int)SIG_ERR)) \
  664.     goto FAIL
  665. # define CATCH_SIGNAL_IGN(SIG) sig = SIG; \
  666.   if (((int)signal(SIG, dns_signal_handler_ign)) == ((int)SIG_ERR)) \
  667.     goto FAIL
  668. #else  /* !PROC2_DEBUG_PRINT */
  669. # define CATCH_SIGNAL_DFL(SIG) \
  670.    if ((int)signal(SIG,SIG_DFL) == ((int)SIG_ERR)) goto FAIL
  671. # define CATCH_SIGNAL_IGN(SIG) \
  672.    if ((int)signal(SIG,SIG_IGN) == ((int)SIG_ERR)) goto FAIL
  673. #endif /* !PROC2_DEBUG_PRINT */
  674.  
  675.   CATCH_SIGNAL_IGN(SIGHUP);
  676.   CATCH_SIGNAL_IGN(SIGINT);
  677.   CATCH_SIGNAL_IGN(SIGQUIT);
  678.   CATCH_SIGNAL_DFL(SIGILL);
  679.   CATCH_SIGNAL_DFL(SIGTRAP);
  680.   CATCH_SIGNAL_DFL(SIGIOT);
  681.   CATCH_SIGNAL_DFL(SIGABRT);
  682. #  ifdef SIGEMT
  683.   CATCH_SIGNAL_DFL(SIGEMT);
  684. #  endif
  685.   CATCH_SIGNAL_DFL(SIGFPE);
  686.   CATCH_SIGNAL_DFL(SIGBUS);
  687.   CATCH_SIGNAL_DFL(SIGSEGV);
  688. #  ifdef SIGSYS
  689.   CATCH_SIGNAL_DFL(SIGSYS);
  690. #  endif
  691.   CATCH_SIGNAL_DFL(SIGPIPE);
  692.   CATCH_SIGNAL_DFL(SIGTERM);    /* ignore this one?  hmm... */
  693.   CATCH_SIGNAL_IGN(SIGUSR1);
  694.   CATCH_SIGNAL_IGN(SIGUSR2);
  695. #  ifdef SIGXCPU
  696.   CATCH_SIGNAL_DFL(SIGXCPU);
  697. #  endif
  698. #  ifdef SIGDANGER
  699.   CATCH_SIGNAL_DFL(SIGDANGER);
  700. #  endif
  701. # undef CATCH_SIGNAL
  702.  
  703.   return;
  704.  
  705.  FAIL:
  706. #ifdef PROC2_DEBUG_PRINT
  707.   sprintf(buf,"\tproc2 (%lu): error handling signal %d",
  708.       (unsigned long) getpid(), sig);
  709.   perror(buf);
  710. #endif /* PROC2_DEBUG_PRINT */
  711.   exit(1);
  712. }
  713.  
  714.  
  715. static void dns_driver_init(int argc, char **argv, int in_fd, int out_fd);
  716. static void dns_driver_main_loop(int in_fd, int out_fd);
  717.  
  718. /* Essentially, this is the main() of the forked dns-helper process.
  719.  */
  720. static void
  721. dns_driver(int argc, char **argv, int in_fd, int out_fd)
  722. {
  723.   /* Might not return, if we fork-and-exec. */
  724.   dns_driver_init(argc, argv, in_fd, out_fd);
  725.  
  726.   /* Shouldn't return until proc1 has closed its end of the pipe. */
  727.   dns_driver_main_loop(in_fd, out_fd);
  728. }
  729.  
  730.  
  731. static void
  732. dns_driver_init(int argc, char **argv, int in_fd, int out_fd)
  733. {
  734. #ifdef CHANGING_ARGV_WORKS
  735.   {
  736.     LOG_PROC2("launched","","\n");
  737.     ERROR! Write me!  (This should overwrite argv.)
  738.   }
  739. #else /* !CHANGING_ARGV_WORKS */
  740.   {
  741.     char proc2_name[] = DNS_PROC2_NAME "\000";
  742.  
  743.     /* If argv[0] is already the proc2 name, then we're already in proc2,
  744.        so there's nothing else to do.
  745.  
  746.        Otherwise, re-exec the running program with a different name.
  747.        Kludge, kludge, kludge.
  748.      */
  749.     if (!strcmp(argv[0], proc2_name))
  750.       {
  751.     LOG_PROC2("dns_driver_init","not respawning.\n","");
  752.       }
  753.     else
  754.       {
  755.     char *new_argv[2];
  756.     new_argv[0] = proc2_name;
  757.     new_argv[1] = 0;
  758.  
  759.     LOG_PROC2("dns_driver_init",
  760.           "execvp(... \"" DNS_PROC2_NAME "\")\n", "");
  761.  
  762.     /* Copy in_fd and out_fd onto stdin/stdout, in order to pass them
  763.        to the newly-exec'ed process. */
  764.     dup2(in_fd, fileno(stdin));
  765.     dup2(out_fd, fileno(stdout));
  766.  
  767.     execvp(argv[0], new_argv);
  768.  
  769.     fprintf(stderr,
  770.         "\nMozilla: execvp(\"%s\") failed!\n"
  771.     "\tThis means that we were unable to fork() the dns-helper process,\n"
  772.     "\tand so host-name lookups will happen in the foreground instead\n"
  773.     "\tof in the background (and therefore won't be interruptible.)\n\n",
  774.         argv[0]);
  775.     exit(0);
  776.       }
  777.   }
  778. #endif /* !CHANGING_ARGV_WORKS */
  779.  
  780.  
  781.   /* Close any streams we're not using.
  782.    */
  783.   if (fileno(stdin) != in_fd && fileno(stdin) != out_fd)
  784.     fclose(stdin);
  785.   if (fileno(stdout) != in_fd && fileno(stdout) != out_fd)
  786.     fclose(stdout);
  787.   /* Never close stderr -- proc2 needs it in case the execvp() fails. */
  788.  
  789.   dns_socks_kludge();
  790.   dns_catch_signals();
  791.  
  792. #ifdef RANDOMIZE_ADDRESSES
  793.   {
  794.     struct timeval tp;
  795.     struct timezone tzp;
  796.     int seed;
  797.     gettimeofday(&tp, &tzp);
  798.     seed = (999*tp.tv_sec) + (1001*tp.tv_usec) + (1003 * getpid());
  799.     SRANDOM(seed);
  800.   }
  801. #endif /* RANDOMIZE_ADDRESSES */
  802.  
  803.  
  804. #if defined(NSPR) || defined(JAVA)
  805. #ifndef NSPR20
  806.   /* Hate hate hate hate!! */
  807.   LOG_PROC2("dns_driver_init","calling PR_Init.\n","");
  808.   PR_Init("dns_helper", 24, 1, 0);
  809. #endif
  810. #endif
  811. }
  812.  
  813.  
  814. static void
  815. dns_driver_main_loop(int in_fd, int out_fd)
  816. {
  817.   int status = 0;
  818.   FILE *in = fdopen(in_fd, "r");
  819.   ASSERT(in);
  820.   if (!in) return;  /* out of memory *already*?  We lost real bad... */
  821.   SET_BUFFERING(in);
  822.  
  823.   while (1)
  824.     {
  825.       fd_set fdset;
  826.       char line[MAXHOSTNAMELEN + 100];
  827.       dns_lookup *obj = 0;
  828.       dns_lookup *next = 0;
  829.  
  830.       FD_ZERO(&fdset);
  831.  
  832.       /* Select on our parent's input stream. */
  833.       FD_SET(in_fd, &fdset);
  834.  
  835.       /* And select on the pipe associated with each child process. */
  836.       if (proc2_queue)
  837.     for (obj = proc2_queue; obj; obj = obj->next)
  838.       {
  839.         int fd;
  840.         ASSERT(obj->fd);
  841.         if (!obj->fd) continue;
  842.         fd = fileno(obj->fd);
  843.         ASSERT(fd >= 0 &&
  844.            fd != in_fd &&
  845.            fd != out_fd &&
  846.            !FD_ISSET(fd, &fdset));
  847.         FD_SET(fd, &fdset);
  848.       }
  849.  
  850. #ifdef PROC2_DEBUG_PRINT
  851.       {
  852.     int i = 0, j = 0;
  853.     fprintf(stderr, "\tproc2 (%lu): entering select(",
  854.         (unsigned long) getpid());
  855.     for (i = 0; i < getdtablehi(); i++)
  856.       if (FD_ISSET(i, &fdset))
  857.         {
  858.           fprintf(stderr, "%s%d", (j == 0 ? "" : ", "), i);
  859.           j++;
  860.         }
  861.     fprintf(stderr, ")\n");
  862.       }
  863. #endif /* PROC2_DEBUG_PRINT */
  864.  
  865.       status = select(getdtablehi(), &fdset, 0, 0, 0);
  866.  
  867.       LOG_PROC2("select returned","","\n");
  868.  
  869.       ASSERT(status > 0);
  870.       if (status <= 0) continue;
  871.  
  872.  
  873.       /* Handle input from our parent.
  874.        */
  875.  
  876.       if (FD_ISSET (in_fd, &fdset))
  877.     {
  878.       /* Read one line from the parent (blocking if necessary, which
  879.          it shouldn't be except for a trivial amount of time.) */
  880.       *line = 0;
  881.       fgets(line, sizeof(line)-1, in);
  882.  
  883.       if (!*line)
  884.         {
  885.           LOG_PROC2("got EOF from proc1; exiting","","\n");
  886.           return;
  887.         }
  888.  
  889.       LOG_PROC2("read command",line,"");
  890.  
  891.       string_trim(line);
  892.  
  893.       if (!strncmp(line, "kill: ", 6))
  894.         {
  895.           long id = 0;
  896.           status = sscanf(line+6, "%ld\n", &id);
  897.           ASSERT(status == 1);
  898.           if (id)
  899.         cancel_lookup(id, out_fd);
  900.         }
  901.       else if (!strncmp(line, "lookup: ", 8))
  902.         {
  903.           char *name = line + 8;
  904.           obj = spawn_lookup_process(name, out_fd);
  905.           /* Write a response to the parent immediately (to assign an id.)
  906.            */
  907.           if (obj)
  908.         sprintf(line, "%d: %lu\n",
  909.             DNS_STATUS_LOOKUP_STARTED,
  910.             obj->id);
  911.           else
  912.         sprintf(line, "%d: %lu\n",
  913.             DNS_STATUS_LOOKUP_NOT_STARTED,
  914.             obj->id);
  915.  
  916.           LOG_PROC2("writing initial",line,"");
  917.           write(out_fd, line, strlen(line));
  918.           FLUSH(out_fd);
  919.         }
  920.     }
  921.  
  922.  
  923.       /* Handle input from our kids.
  924.        */
  925.  
  926.       for (obj = proc2_queue; obj; obj = next)
  927.     {
  928.       next=obj->next;
  929.       if (FD_ISSET(fileno(obj->fd), &fdset))
  930.         {
  931.           pid_t pid = obj->pid;
  932.           pid_t pid2;
  933.  
  934.           /* Read one line from the subprocess (blocking if necessary,
  935.          which it shouldn't be except for a trivial amount of time.) */
  936.           *line = 0;
  937.           fgets(line, sizeof(line)-1, obj->fd);
  938.  
  939. #ifdef PROC2_DEBUG_PRINT
  940.           fprintf(stderr, "\tproc2 (%lu): read from proc3 (%ld): %s",
  941.               (unsigned long) getpid(), obj->id, line);
  942. #endif /* PROC2_DEBUG_PRINT */
  943.  
  944.           handle_subproc_response(obj, line, out_fd);
  945.  
  946.           /* `obj' has now been freed.  After having written one line, the
  947.          process should exit.  Wait for it now (we saved `pid' before
  948.          `obj' got freed.)
  949.          
  950.          The thing that confuses me is, if I *don't* do the waitpid()
  951.          here, zombies don't seem to get left around.  I expected them
  952.          to, and don't know why they don't.  (On Irix 6.2, at least.)
  953.          */
  954.           pid2 = waitpid(pid, 0, 0);
  955. #ifndef NSPR20
  956.         /*
  957.          * nspr20 library catches the SIGCLD signal so that it can terminate the
  958.          * application when an sproc terminates abnormally. Sometimes the signal
  959.          * handler may be called before the call to waitpid above and in that case
  960.          * waitpid will fail with a ECHILD error.
  961.          */
  962.           ASSERT(pid == pid2);
  963. #endif
  964.         }
  965.     }
  966.     }
  967.  
  968.   ASSERT(0);   /* not reached */
  969. }
  970.  
  971.  
  972.  
  973. /* Process 1 (the user-called code.)
  974.  */
  975.  
  976.  
  977. static FILE *dns_in_fd = 0;
  978. static FILE *dns_out_fd = 0;
  979.  
  980.  
  981. typedef struct queued_response {
  982.   long id;
  983.   int (*cb) (void *id, void *closure, int status, const char *result);
  984.   void *closure;
  985.   struct queued_response *next, *prev;
  986. } queued_response;
  987.  
  988. static queued_response *proc1_queue = 0;
  989.  
  990.  
  991. static long
  992. proc1_queue_length(void)
  993. {
  994.   queued_response *obj;
  995.   long i = 0;
  996.   for (obj = proc1_queue; obj; obj = obj->next) i++;
  997.   return i;
  998. }
  999.  
  1000.  
  1001. /* Call this from main() to initialize the async DNS library.
  1002.    Returns a file descriptor that should be selected for, or
  1003.    negative if something went wrong.  Pass it the argc/argv
  1004.    that your `main' was called with (it needs these pointers
  1005.    in order to give its forked subprocesses sensible names.)
  1006.  */
  1007. int
  1008. DNS_SpawnProcess(int argc, char **argv)
  1009. {
  1010.   static int already_spawned_p = 0;
  1011.   pid_t forked;
  1012.   int infds[2];        /* "in" and "out" are from the p.o.v. of proc2. */
  1013.   int outfds[2];    /* one reads from [0] and writes to [1]. */
  1014.  
  1015.   ASSERT(!already_spawned_p);
  1016.   if (already_spawned_p) return -1;
  1017.   already_spawned_p = 1;
  1018.  
  1019. #ifndef CHANGING_ARGV_WORKS
  1020.   if (!strcmp(argv[0], DNS_PROC2_NAME))
  1021.     {
  1022.       /* Oh look, we're already in proc2, so don't fork again.
  1023.      Assume our side of the pipe is on stdin/stdout.
  1024.        */
  1025.       LOG_PROC2("DNS_SpawnProcess","reconnecting to stdin/stdout.\n","");
  1026.  
  1027.       /* doesn't return until the other side closes the pipe. */
  1028.       dns_driver(argc, argv, fileno(stdin), fileno(stdout));
  1029.       exit(0);
  1030.     }
  1031. #endif /* !CHANGING_ARGV_WORKS */
  1032.  
  1033.   LOG_PROC1("DNS_SpawnProcess","forking proc2.\n","");
  1034.  
  1035.   if (pipe (infds))
  1036.     {
  1037.       return -1; /* pipe error: return better error code? */
  1038.     }
  1039.   if (pipe (outfds))
  1040.     {
  1041.       close(infds[0]);
  1042.       close(infds[1]);
  1043.       return -1; /* pipe error: return better error code? */
  1044.     }
  1045.  
  1046.   switch (forked = fork ())
  1047.     {
  1048.     case -1:
  1049.       {
  1050.     close (infds[0]);
  1051.     close (infds[1]);
  1052.     close (outfds[0]);
  1053.     close (outfds[1]);
  1054.     return -1; /* fork error: return better error code? */
  1055.       }
  1056.     break;
  1057.  
  1058.     case 0:    /* This is the forked process. */
  1059.       {
  1060.     /* Close the other sides of the pipes (used by the main fork.) */
  1061.     close (infds[1]);
  1062.     close (outfds[0]);
  1063.  
  1064.     /* doesn't return until the other side closes the pipe. */
  1065.     dns_driver(argc, argv, infds[0], outfds[1]);
  1066.  
  1067.     close (infds[0]);
  1068.     close (outfds[1]);
  1069.     exit(-1);
  1070.       }
  1071.     break;
  1072.  
  1073.     default:
  1074.       {
  1075.     /* This is the "old" process (subproc pid is in `forked'.)
  1076.        (Save that somewhere?)
  1077.      */
  1078.  
  1079.     /* This is the file descriptor we created for the benefit
  1080.        of the child process - we don't need it in the parent. */
  1081.     close (infds[0]);
  1082.     close (outfds[1]);
  1083.  
  1084.     /* Set up FILE objects for the proc3 side of the pipes.
  1085.        (Note that the words "out" in `dns_out_fd' and `outfds'
  1086.        have opposite senses here.)
  1087.      */
  1088.     dns_out_fd = fdopen(infds[1], "w");
  1089.     dns_in_fd = fdopen(outfds[0], "r");
  1090.  
  1091.     SET_BUFFERING(dns_out_fd);
  1092.     SET_BUFFERING(dns_in_fd);
  1093.  
  1094.     return fileno(dns_in_fd);   /* ok! */
  1095.       }
  1096.     break;
  1097.     }
  1098. }
  1099.  
  1100.  
  1101. static queued_response *
  1102. new_queued_response (int (*cb) (void *, void *closure, int, const char *),
  1103.              void *closure)
  1104. {
  1105.   queued_response *obj;
  1106.  
  1107.   obj = (queued_response *) malloc(sizeof(*obj));
  1108.   if (!obj) return 0;
  1109.   memset(obj, 0, sizeof(*obj));
  1110.   obj->id = 0;
  1111.   obj->cb = cb;
  1112.   obj->closure = closure;
  1113.  
  1114.   ASSERT(!proc1_queue || proc1_queue->prev == 0);
  1115.   obj->next = proc1_queue;
  1116.   if (proc1_queue)
  1117.     proc1_queue->prev = obj;
  1118.   proc1_queue = obj;
  1119.  
  1120.   return obj;
  1121. }
  1122.  
  1123.  
  1124. /* Frees the object associated with a host lookup on which proc1 is waiting,
  1125.    and runs the callback function.  The returned value is that of the callback.
  1126.  */
  1127. static int
  1128. free_queued_response (queued_response *obj, int status, const char *result)
  1129. {
  1130.   long id = obj->id;
  1131.   int (*cb) (void *id, void *closure, int status, const char *result)
  1132.     = obj->cb;
  1133.   void *closure = obj->closure;
  1134.  
  1135.   if( -1 == id ) return 0;
  1136.  
  1137.   if (obj->prev)
  1138.     {
  1139.       ASSERT(obj->prev->next != obj->next);
  1140.       obj->prev->next = obj->next;
  1141.     }
  1142.   if (obj->next)
  1143.     {
  1144.       ASSERT(obj->next->prev != obj->prev);
  1145.       obj->next->prev = obj->prev;
  1146.     }
  1147.   if (proc1_queue == obj)
  1148.     proc1_queue = obj->next;
  1149.  
  1150.   ASSERT(closure != (void *) obj);
  1151.   memset(obj, ~0, sizeof(*obj));
  1152.   free(obj);
  1153.  
  1154.   ASSERT(status < -100 && status > -700);
  1155.   if (status == -DNS_STATUS_LOOKUP_OK) status = 1;
  1156.   else if (status == -DNS_STATUS_KILL_FAILED) status = 0;
  1157.  
  1158.   if (cb)
  1159.     return cb((void *) id, closure, status, result);
  1160.   else
  1161.     return 0;
  1162. }
  1163.  
  1164.  
  1165. /* Given a line which is an asynchronous response from proc2, find the
  1166.    associated lookup object, run the callback, return the status that
  1167.    the callback returned, and free the queued_response object.
  1168.  */
  1169. static int
  1170. handle_async_response(char *line)
  1171. {
  1172.   int i;
  1173.   int code = 0;
  1174.   queued_response *obj = 0;
  1175.   long id = 0;
  1176.   int iip[4];
  1177.  
  1178.   ASSERT(line);
  1179.   if (!line || !*line) return -1;
  1180.  
  1181.   i = sscanf(line, "%d: %lu: %d.%d.%d.%d\n",
  1182.          &code, &id,
  1183.          &iip[0], &iip[1], &iip[2], &iip[3]);
  1184.   if (i != 6)
  1185.     {
  1186.       i = sscanf(line, "%d: %lu:", &code, &id);
  1187.       if (i != 2)
  1188.     {
  1189.       i = sscanf(line, "%d:", &code);
  1190.       if (i != 1)
  1191.         code = DNS_STATUS_INTERNAL_ERROR;
  1192.     }
  1193.     }
  1194.  
  1195.   if (id)
  1196.     for (obj = proc1_queue; obj; obj = obj->next)
  1197.       if (obj->id == id) break;
  1198.  
  1199.   if (!obj)        /* got an id that we don't know about? */
  1200.     return -1;
  1201.  
  1202.   switch (code)
  1203.     {
  1204.     case DNS_STATUS_LOOKUP_OK:        /* "1xx: id: a.b.c.d\n" */
  1205.       {
  1206.     unsigned char ip[5];
  1207.     ip[0] = iip[0]; ip[1] = iip[1]; ip[2] = iip[2]; ip[3] = iip[3];
  1208.     ip[4] = 0;  /* just in case */
  1209.     return free_queued_response(obj, -DNS_STATUS_LOOKUP_OK, (char *) ip);
  1210.     break;
  1211.       }
  1212.     case DNS_STATUS_LOOKUP_FAILED:    /* "5xx: id: host not found\n" */
  1213.     default:
  1214.       {
  1215.     char *msg = 0;
  1216.     ASSERT(code == DNS_STATUS_LOOKUP_FAILED);
  1217.     msg = strchr(line, ':');
  1218.     if (msg)
  1219.       {
  1220.         char *s = strchr(msg, ':');
  1221.         if (s) msg = s+1;
  1222.       }
  1223.     else
  1224.       {
  1225.         msg = line;
  1226.       }
  1227.  
  1228.     code = (code > 100 ? -code : -DNS_STATUS_INTERNAL_ERROR);
  1229.     free_queued_response(obj, code, string_trim(msg));
  1230.     return code;
  1231.     break;
  1232.       }
  1233.     }
  1234.   ASSERT(0); /* not reached */
  1235. }
  1236.  
  1237.  
  1238.  
  1239. /* Kick off an async DNS lookup;
  1240.    The returned value is an id representing this transaction;
  1241.     the result_callback will be run (in the main process) when we
  1242.     have a result.  Returns negative if something went wrong.
  1243.    If `status' is negative,`result' is an error message.
  1244.    If `status' is positive, `result' is a 4-character string of
  1245.    the IP address.
  1246.    If `status' is 0, then the lookup was prematurely aborted
  1247.     via a call to DNS_AbortHostLookup().
  1248.  */
  1249. int
  1250. DNS_AsyncLookupHost(const char *name,
  1251.             int (*result_callback) (void *id,
  1252.                         void *closure,
  1253.                         int status,
  1254.                         const char *result),
  1255.             void *closure,
  1256.             void **id_return)
  1257. {
  1258.   char buf[MAXHOSTNAMELEN + 100];
  1259.   queued_response *obj = 0;
  1260.   int code = 0;
  1261.   int i;
  1262.   char *s;
  1263.  
  1264.   ASSERT(result_callback);
  1265.   if (!result_callback) return -1;
  1266.   ASSERT(id_return);
  1267.   if (!id_return) return -1;
  1268.   *id_return = 0;
  1269.  
  1270.   ASSERT(name);
  1271.   if (!name || !*name) return -1;  /* invalid host name: better error code? */
  1272.   if (strchr(name, '\n')) return -1; /* ditto */
  1273.   if (strchr(name, '\r')) return -1;
  1274.  
  1275.   if (strlen(name) > MAXHOSTNAMELEN)
  1276.     return -1; /* host name too long: return better error code? */
  1277.  
  1278.   obj = new_queued_response(result_callback, closure);
  1279.   if (!obj) return -1; /* out of memory: return better error code? */
  1280.  
  1281.  
  1282.   LOG_PROC1("writing: lookup",name,"\n");
  1283.   fprintf(dns_out_fd, "lookup: %s\n", name);
  1284.   fflush(dns_out_fd);
  1285.   FLUSH(fileno(dns_out_fd));
  1286.  
  1287.   /* We have just written a "lookup:" command to proc2.
  1288.      As soon as this command is received, proc2 will write a 2xx response
  1289.      back to us, ACKing it, and giving us an ID.  We must wait for that
  1290.      response in order to return the ID to our caller; we block waiting
  1291.      for it, knowing that it won't be long in coming.
  1292.  
  1293.      However, there could be other data in the pipe already -- the final
  1294.      results of previously launched commands (1xx responses or 5xx responses.)
  1295.      So we read commands, and process them, until we find the one we're
  1296.      looking for.
  1297.  
  1298.      Under no circumstances can there be more than one 2xx response in the
  1299.      pipe at a time (since this is the only place a command is issued that
  1300.      can cause a 2xx response to be generated, and each call waits for it.)
  1301.    */
  1302.  
  1303. AGAIN:
  1304.  
  1305.   *buf = 0;
  1306.   fgets(buf, sizeof(buf)-1, dns_in_fd);
  1307.   LOG_PROC1("read (lookup)",buf,"");
  1308.  
  1309.   obj->id = 0;
  1310.  
  1311.   if (!*buf)    /* EOF, perhaps? */
  1312.     {
  1313.       code = DNS_STATUS_INTERNAL_ERROR;
  1314.     }
  1315.   else
  1316.     {
  1317.       i = sscanf(buf, "%d:", &code);
  1318.       ASSERT(i == 1);
  1319.       if (code <= 100) code = DNS_STATUS_INTERNAL_ERROR;
  1320.     }
  1321.  
  1322.   switch (code)
  1323.     {
  1324.     case DNS_STATUS_LOOKUP_STARTED:        /* 2xx: id\n" */
  1325.       i = sscanf(buf, "%d: %lu\n", &code, &obj->id);
  1326.       ASSERT(i == 2);
  1327.       *id_return = (void *) obj->id;
  1328.       return 0;
  1329.       break;
  1330.  
  1331.     case DNS_STATUS_LOOKUP_OK:            /* 1xx or 5xx which is an */
  1332.     case DNS_STATUS_LOOKUP_FAILED:        /* async response (not for us) */
  1333.       handle_async_response(buf);
  1334.       goto AGAIN;
  1335.       break;
  1336.  
  1337.     case DNS_STATUS_LOOKUP_NOT_STARTED:
  1338.     default:                    /* 5xx: error msg\n"
  1339.                            Presumably this is for us.
  1340.                            (If not, there's a bug...) */
  1341.       ASSERT(code == DNS_STATUS_LOOKUP_NOT_STARTED);
  1342.       s = strchr(buf, ':');
  1343.       if (s) s++;
  1344.       code = (code > 100 ? -code : -DNS_STATUS_INTERNAL_ERROR);
  1345.       code = free_queued_response(obj, code, (s ? string_trim(s) : "???"));
  1346.       obj = 0;
  1347.       return code;
  1348.       break;
  1349.     }
  1350.   ASSERT(0); /* not reached */
  1351.   return -1;
  1352. }
  1353.  
  1354.  
  1355. /* Prematurely give up on the given host-lookup transaction.
  1356.    The `token' is what was returned by DNS_AsyncLookupHost.
  1357.    This causes the result_callback to be called with a negative
  1358.    status.
  1359.  */
  1360. int
  1361. DNS_AbortHostLookup(void *token)
  1362. {
  1363.   char buf[MAXHOSTNAMELEN + 100];
  1364.   int code = 0;
  1365.   int i;
  1366.   long id = (int) token;
  1367.   long id2 = 0;
  1368.   queued_response *obj = 0;
  1369.   char *s;
  1370.  
  1371.   for (obj = proc1_queue; obj; obj = obj->next)
  1372.     if (obj->id == id) break;
  1373.  
  1374.   if (!obj) return -1;
  1375.  
  1376. #ifdef PROC1_DEBUG_PRINT
  1377.   fprintf(stderr, "\tproc1 (%lu): writing: kill: %lu (ql=%ld)\n",
  1378.       (unsigned long) getpid(), id, proc1_queue_length());
  1379. #endif /* PROC1_DEBUG_PRINT */
  1380.  
  1381.   fprintf(dns_out_fd, "kill: %lu\n", id);
  1382.   fflush(dns_out_fd);
  1383.   FLUSH(fileno(dns_out_fd));
  1384.  
  1385.  
  1386.   /* We have just written a "kill:" command to proc2.
  1387.      As soon as this command is received, proc2 will write a 1xx or 5xx
  1388.      response back to us, ACKing it.  We must wait for that response in
  1389.      order to return an error code to our caller; we block waiting for it,
  1390.      knowing that it won't be long in coming.
  1391.  
  1392.      However, there could be other data in the pipe already -- the final
  1393.      results of previously launched commands (1xx responses or 5xx responses.)
  1394.      So we read commands, and process them, until we find the one we're
  1395.      looking for.
  1396.  
  1397.      Under no circumstances can there be more than one kill-response in the
  1398.      pipe at a time (since this is the only place a command is issued that
  1399.      can cause a kill-response response to be generated, and each call waits
  1400.      for it.)
  1401.    */
  1402.  
  1403. AGAIN:
  1404.  
  1405.   *buf = 0;
  1406.   fgets(buf, sizeof(buf)-1, dns_in_fd);
  1407.   LOG_PROC1("read (kill)",buf,"");
  1408.  
  1409.   i = sscanf(buf, "%d:", &code);
  1410.   ASSERT(i == 1);
  1411.   if (code <= 100) code = DNS_STATUS_INTERNAL_ERROR;
  1412.  
  1413.   switch (code)
  1414.     {
  1415.     case DNS_STATUS_KILLED_OK:
  1416.       return free_queued_response(obj, -DNS_STATUS_KILLED_OK, "killed");
  1417.       break;
  1418.  
  1419.     case DNS_STATUS_LOOKUP_OK:            /* 1xx or 5xx which is an */
  1420.     case DNS_STATUS_LOOKUP_FAILED:        /* async response (not for us) */
  1421.  
  1422.       /* Actually, it might be for us -- if we've issued a kill, but the
  1423.      success/failure for this same code raced in before the response
  1424.      to the kill (a kill-failure, presumably) then ignore this response
  1425.      (wait for the kill-failure instead.) */
  1426.       id2 = 0;
  1427.       i = sscanf(buf, "%d: %lu:", &code, &id2);
  1428.       ASSERT(i == 2);
  1429.       if (i == 2 && id2 == id) {
  1430.           LOG_PROC1("Bug #87736 (1)",buf,"");
  1431.           handle_async_response(buf);
  1432.     goto AGAIN;
  1433.       }
  1434.  
  1435.       /* Not for us -- let it through. */
  1436.       handle_async_response(buf);
  1437.       goto AGAIN;
  1438.       break;
  1439.  
  1440.     case DNS_STATUS_KILL_FAILED:
  1441.     default:                    /* 5xx: error msg\n"
  1442.                            Presumably this is for us.
  1443.                            (If not, there's a bug...) */
  1444.       ASSERT(code == DNS_STATUS_KILL_FAILED);
  1445.       s = strchr(buf, ':');
  1446.       if (s)
  1447.     {
  1448.       char *s2 = strchr(++s, ':');
  1449.       if (s2) s = s2+1;
  1450.     }
  1451.       code = (code > 100 ? -code : -DNS_STATUS_INTERNAL_ERROR);
  1452.       free_queued_response(obj, code, (s ? string_trim(s) : "???"));
  1453.       LOG_PROC1("Bug #87736 (2)",buf,"");
  1454.       return code;
  1455.       break;
  1456.     }
  1457.   ASSERT(0); /* not reached */
  1458.   return -1;
  1459. }
  1460.  
  1461.  
  1462.  
  1463. /* The main select() loop of your program should call this when the fd
  1464.    that was returned by DNS_SpawnProcess comes active.  This may cause
  1465.    some of the result_callback functions to run.
  1466.  
  1467.    If this returns negative, then a fatal error has happened, and you
  1468.    should close `fd' and not select() for it again.  Call gethostbyname()
  1469.    in the foreground or something.
  1470.  */
  1471. int
  1472. DNS_ServiceProcess(int fd)
  1473. {
  1474.   char buf[MAXHOSTNAMELEN + 100];
  1475.   char *s = 0;
  1476.   int status = 0;
  1477.   int final_status = 0;
  1478.  
  1479. AGAIN:
  1480.  
  1481.   ASSERT(fd == fileno(dns_in_fd));
  1482.   if (fd != fileno(dns_in_fd)) return -1;
  1483.  
  1484.   ASSERT(final_status <= 0);
  1485.  
  1486.   /* Poll to see if input is available on fd.
  1487.      NOTE: This only works because we've marked the FILE* object wrapped
  1488.      around this fd as being fully unbuffered!!
  1489.    */
  1490.   {
  1491.     fd_set fdset;
  1492.     struct timeval t = { 0L, 0L, };
  1493.     FD_ZERO(&fdset);
  1494.     FD_SET(fd, &fdset);
  1495.     status = select(fd+1, &fdset, 0, 0, &t);
  1496.     if (status < 0 && final_status == 0)
  1497.       final_status = status;
  1498.     if (status <= 0)  /* error, or no input ready */
  1499.       return final_status;
  1500.   }
  1501.  
  1502.   *buf = 0;
  1503.   s = fgets(buf, sizeof(buf)-1, dns_in_fd);
  1504.  
  1505.   if (s && *s)
  1506.     {
  1507.       LOG_PROC1("read (async)",buf,"");
  1508.     }
  1509.   else
  1510.     {
  1511.       /* #### If there's nothing to read, the socket must be closed? */
  1512.       LOG_PROC1("read (async)","nothing -- returning error\n","");
  1513.       return (final_status < 0 ? final_status : -1);
  1514.     }
  1515.  
  1516.   /* call all pending procs, but remember the first error code that
  1517.      one of them returned.
  1518.      */
  1519.   status = handle_async_response(buf);
  1520.   status = 0; /* #### */
  1521.   if (status < 0 && final_status == 0)
  1522.     final_status = status;
  1523.  
  1524.   /* see if there is more to read. */
  1525.   goto AGAIN;
  1526. }
  1527.  
  1528.  
  1529.  
  1530. #ifdef DEBUG
  1531. void
  1532. print_fdset(fd_set *fdset)
  1533. {
  1534.   int i;
  1535.   int any = 0;
  1536.   if (!fdset) return;
  1537.   for (i = 0; i < getdtablesize(); i++)
  1538.     {
  1539.       if (FD_ISSET(i, fdset))
  1540.     {
  1541.       if (any) fprintf(stderr, ", ");
  1542.       any = 1;
  1543.       fprintf(stderr, "%d", i);
  1544.     }
  1545.     }
  1546. }
  1547.  
  1548. void
  1549. print_fd(int fd)
  1550. {
  1551.   int status;
  1552.   fd_set fdset;
  1553.   struct timeval t = { 0L, 0L, };
  1554.  
  1555.   fprintf(stderr, "  %d:\n", fd);
  1556.  
  1557.   FD_ZERO(&fdset);
  1558.   FD_SET(fd, &fdset);
  1559.   status = select(fd+1, &fdset, 0, 0, &t);
  1560.   if (status == 0)
  1561.     fprintf(stderr, "    readable (no input)\n");
  1562.   else if (status == 1)
  1563.     fprintf(stderr, "    readable (pending)\n");
  1564.   else
  1565.     fprintf(stderr, "    unreadable (error %d)\n", status);
  1566.  
  1567.   FD_ZERO(&fdset);
  1568.   FD_SET(fd, &fdset);
  1569.   status = select(fd+1, 0, &fdset, 0, &t);
  1570.   if (status == 0)
  1571.     fprintf(stderr, "    writeable (no input)\n");
  1572.   else if (status == 1)
  1573.     fprintf(stderr, "    writeable (pending)\n");
  1574.   else
  1575.     fprintf(stderr, "    unwriteable (error %d)\n", status);
  1576.  
  1577.   FD_ZERO(&fdset);
  1578.   FD_SET(fd, &fdset);
  1579.   status = select(fd+1, 0, 0, &fdset, &t);
  1580.   if (status == 0)
  1581.     fprintf(stderr, "    exceptionable (no input)\n");
  1582.   else if (status == 1)
  1583.     fprintf(stderr, "    exceptionable (pending)\n");
  1584.   else
  1585.     fprintf(stderr, "    unexceptionable (error %d)\n", status);
  1586. }
  1587.  
  1588.  
  1589. void
  1590. print_fdsets(fd_set *read, fd_set *write, fd_set *except)
  1591. {
  1592.   int i;
  1593.   fd_set all;
  1594.   FD_ZERO(&all);
  1595.  
  1596.   fprintf(stderr, "rd: "); print_fdset(read);
  1597.   fprintf(stderr, "\nwr: "); print_fdset(write);
  1598.   fprintf(stderr, "\nex: "); print_fdset(except);
  1599.   fprintf(stderr, "\n");
  1600.  
  1601.   if (read)
  1602.     for (i = 0; i < getdtablesize(); i++)
  1603.       if (FD_ISSET(i, read)) FD_SET(i, &all);
  1604.   if (write)
  1605.     for (i = 0; i < getdtablesize(); i++)
  1606.       if (FD_ISSET(i, write)) FD_SET(i, &all);
  1607.   if (except)
  1608.     for (i = 0; i < getdtablesize(); i++)
  1609.       if (FD_ISSET(i, except)) FD_SET(i, &all);
  1610.  
  1611.   for (i = 0; i < getdtablesize(); i++)
  1612.     if (FD_ISSET(i, &all)) print_fd(i);
  1613. }
  1614.  
  1615. #endif /* DEBUG */
  1616.  
  1617.  
  1618.  
  1619.  
  1620. #ifdef STANDALONE
  1621.  
  1622. typedef struct test_id_cons {
  1623.   char *name;
  1624.   void *id;
  1625.   struct test_id_cons *next;
  1626. } test_id_cons;
  1627. static test_id_cons *test_list = 0;
  1628.  
  1629. static int
  1630. test_cb (void *id, void *closure, int status, const char *result)
  1631. {
  1632.   const char *argv0 = (char *) closure;
  1633.   test_id_cons *cons = test_list;
  1634.  
  1635.   while(cons)
  1636.     {
  1637.       if (cons->id == id) break;
  1638.       cons = cons->next;
  1639.       ASSERT(cons);
  1640.     }
  1641.  
  1642.   if (status == 1)
  1643.     {
  1644.       unsigned char *ip = (unsigned char *) result;
  1645.       fprintf(stderr, "%s: %s = %d.%d.%d.%d\n",
  1646.           argv0, cons->name, ip[0], ip[1], ip[2], ip[3]);
  1647.     }
  1648.   else
  1649.     {
  1650.       fprintf(stderr, "%s: %s: error %d: %s\n",
  1651.           argv0, cons->name, status, result);
  1652.     }
  1653.   return 0;
  1654. }
  1655.  
  1656. int
  1657. main (int argc, char **argv)
  1658. {
  1659.   char buf[MAXHOSTNAMELEN + 100];
  1660.   int fd = DNS_SpawnProcess(argc, argv);
  1661.  
  1662.   if (fd <= 0)
  1663.     {
  1664.       fprintf(stderr, "%s: dns init failed\n", argv[0]);
  1665.       exit(-1);
  1666.     }
  1667.  
  1668.   fprintf(stderr,
  1669.       "Type host names to spawn lookups.\n"
  1670.       "Type `abort ID' to kill one off.\n"
  1671.       "Type `quit' when done.\n\n");
  1672.  
  1673.   while(1)
  1674.     {
  1675.       int status = 0;
  1676.       fd_set fdset;
  1677.  
  1678.       FD_ZERO(&fdset);
  1679.       FD_SET(fd, &fdset);
  1680.       FD_SET(fileno(stdin), &fdset);
  1681.  
  1682.       status = select(getdtablehi(), &fdset, 0, 0, 0);
  1683.       if (status <= 0)
  1684.     {
  1685.       fprintf(stderr, "%s: select() returned %d\n", argv[0], status);
  1686.       exit(-1);
  1687.     }
  1688.  
  1689.       if (!FD_ISSET(fd, &fdset) &&
  1690.       !FD_ISSET(fileno(stdin), &fdset))
  1691.     {
  1692.       fprintf(stderr, "%s: neither fd is set?\n", argv[0]);
  1693.       exit(-1);
  1694.     }
  1695.  
  1696.       if (FD_ISSET(fd, &fdset))
  1697.     {
  1698.       status = DNS_ServiceProcess(fd);
  1699.       if (status < 0)
  1700.         {
  1701.           fprintf(stderr, "%s: DNS_ServiceProcess() returned %d\n",
  1702.               argv[0], status);
  1703.           exit(-1);
  1704.         }
  1705.     }
  1706.  
  1707.       if (FD_ISSET(fileno(stdin), &fdset))
  1708.     {
  1709.       /* Read a line from the user. */
  1710.       char *line = fgets(buf, sizeof(buf)-1, stdin);
  1711.  
  1712.       line = string_trim(line);
  1713.  
  1714.       if (!strcmp(line, "quit") || !strcmp(line, "exit"))
  1715.         {
  1716.           fprintf(stderr, "buh bye now\n");
  1717.           exit(0);
  1718.         }
  1719.       else if (!strncmp(line, "abort ", 6))
  1720.         {
  1721.           long id;
  1722.           status = sscanf(line+6, "%lu\n", &id);
  1723.           if (status != 1)
  1724.         {
  1725.           test_id_cons *cons = test_list;
  1726.           while(cons)
  1727.             {
  1728.               if (!strcmp(cons->name, line+6)) break;
  1729.               cons = cons->next;
  1730.             }
  1731.           if (cons)
  1732.             id = (long) cons->id;
  1733.           else
  1734.             {
  1735.               fprintf(stderr, "%s: %s is not a known host or id.\n",
  1736.                   argv[0], line+6);
  1737.               continue;
  1738.             }
  1739.         }
  1740.  
  1741.           status = DNS_AbortHostLookup((void *)id);
  1742.           if (status < 0)
  1743.         fprintf(stderr, "%s: DNS_AbortHostLookup(%ld) returned %d\n",
  1744.             argv[0], id, status);
  1745.         }
  1746.       else if (strchr(line, ' ') || strchr(line, '\t'))
  1747.         {
  1748.           fprintf(stderr, "%s: unrecognized command %s.\n",
  1749.                   argv[0], line);
  1750.         }
  1751.       else
  1752.         {
  1753.           void *id = 0;
  1754.           test_id_cons *cons = 0;
  1755.  
  1756.           fprintf(stderr, "%s: looking up %s...", argv[0], line);
  1757.           status = DNS_AsyncLookupHost(line, test_cb, argv[0], &id);
  1758.           if (status == 0)
  1759.         fprintf(stderr, " id = %lu.\n", (long)id);
  1760.           else
  1761.         fprintf(stderr,
  1762.               "\n%s: DNS_AsyncLookupHost(%s) returned %d (id = %lu)\n",
  1763.             argv[0], line, status, (long)id);
  1764.  
  1765.           cons = (test_id_cons *) malloc(sizeof(*cons));
  1766.           cons->name = strdup(line);
  1767.           cons->id = id;
  1768.           cons->next = test_list;
  1769.           test_list = cons;
  1770.         }
  1771.     }
  1772.     }
  1773. }
  1774.  
  1775. #endif /* STANDALONE */
  1776. #endif /* UNIX_ASYNC_DNS && XP_UNIX */
  1777.