about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>2012-08-14 23:18:41 -0400
committerThomas E. Dickey <dickey@invisible-island.net>2012-08-14 23:18:41 -0400
commitcca7f473d4ac3d75d28ffbe7361c97f1ed80afb4 (patch)
treea2d02b51ca57a0409b0ac8952c075bd7f754a9fc /WWW/Library/Implementation
parent4fe78157f9d08e4bf8de41f47ee117b4b9383814 (diff)
downloadlynx-snapshots-cca7f473d4ac3d75d28ffbe7361c97f1ed80afb4.tar.gz
snapshot of project "lynx", label v2-8-8dev_12m
Diffstat (limited to 'WWW/Library/Implementation')
-rw-r--r--WWW/Library/Implementation/HTTCP.c978
1 files changed, 582 insertions, 396 deletions
diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c
index 2723422c..616c1f0c 100644
--- a/WWW/Library/Implementation/HTTCP.c
+++ b/WWW/Library/Implementation/HTTCP.c
@@ -1,5 +1,5 @@
 /*
- * $LynxId: HTTCP.c,v 1.107 2012/02/09 12:36:45 tom Exp $
+ * $LynxId: HTTCP.c,v 1.112 2012/08/14 23:18:41 tom Exp $
  *
  *			Generic Communication Code		HTTCP.c
  *			==========================
@@ -349,6 +349,15 @@ BOOL valid_hostname(char *name)
 }
 
 #ifdef NSL_FORK
+/* for transfer of status from child to parent: */
+typedef struct _statuses {
+    size_t rehostentlen;
+    int h_length;
+    int child_errno;		/* sometimes useful to pass this on */
+    int child_h_errno;
+    BOOL h_errno_valid;
+} STATUSES;
+
 /*
  *  Function to allow us to be killed with a normal signal (not
  *  SIGKILL), but don't go through normal libc exit() processing, which
@@ -373,11 +382,12 @@ int lynx_nsl_status = HT_OK;
  *  addresses, in a format inspired by gdb's print format. - kw
  */
 static void dump_hostent(const char *msgprefix,
-			 const LYNX_HOSTENT *phost)
+			 void *data)
 {
     if (TRACE) {
 	int i;
 	char **pcnt;
+	const LYNX_HOSTENT *phost = data;
 
 	CTRACE((tfp, "%s: %p ", msgprefix, (const void *) phost));
 	if (phost) {
@@ -633,7 +643,7 @@ extern int h_errno;
  * struct via a pipe in one read -TD
  */
 #ifdef NSL_FORK
-static unsigned readit(int fd, char *buffer, size_t length)
+static unsigned read_bytes(int fd, char *buffer, size_t length)
 {
     unsigned result = 0;
 
@@ -650,13 +660,414 @@ static unsigned readit(int fd, char *buffer, size_t length)
     }
     return result;
 }
+
+static BOOL setup_nsl_fork(void (*really) (char *, char *, STATUSES *, void *),
+			   unsigned (*readit) (int, char *, size_t),
+			   void (*dumpit) (const char *, void *),
+			   char *host,
+			   char *port,
+			   void *rehostent)
+{
+    STATUSES statuses;
+
+    /*
+     * fork-based gethostbyname() with checks for interrupts.
+     * - Tom Zerucha (tz@execpc.com) & FM
+     */
+    int got_rehostent = 0;
+
+#if HAVE_SIGACTION
+    sigset_t old_sigset;
+    sigset_t new_sigset;
+#endif
+    /*
+     * Pipe, child pid, status buffers, start time, select() control
+     * variables.
+     */
+    int fpid, waitret;
+    int pfd[2], selret;
+    unsigned readret;
+
+#ifdef HAVE_TYPE_UNIONWAIT
+    union wait waitstat;
+
+#else
+    int waitstat = 0;
+#endif
+    time_t start_time = time((time_t *) 0);
+    fd_set readfds;
+    struct timeval one_second;
+    long dns_patience = 30;	/* how many seconds will we wait for DNS? */
+    int child_exited = 0;
+
+    memset(&statuses, 0, sizeof(STATUSES));
+    statuses.h_errno_valid = NO;
+
+    /*
+     * Reap any children that have terminated since last time through.
+     * This might include children that we killed, then waited with WNOHANG
+     * before they were actually ready to be reaped.  (Should be max of 1
+     * in this state, but the loop is safe if waitpid() is implemented
+     * correctly:  returns 0 when children exist but none have exited; -1
+     * with errno == ECHILD when no children.) -BL
+     */
+    do {
+	waitret = waitpid(-1, 0, WNOHANG);
+    } while (waitret > 0 || (waitret == -1 && errno == EINTR));
+    waitret = 0;
+
+    IGNORE_RC(pipe(pfd));
+
+#if HAVE_SIGACTION
+    /*
+     * Attempt to prevent a rare situation where the child could execute
+     * the Lynx signal handlers because it gets killed before it even has a
+     * chance to reset its handlers, resulting in bogus 'Exiting via
+     * interrupt' message and screen corruption or worse.
+     * Should that continue to be reported, for systems without
+     * sigprocmask(), we need to find a different solutions for those.  -
+     * kw 19990430
+     */
+    sigemptyset(&new_sigset);
+    sigaddset(&new_sigset, SIGTERM);
+    sigaddset(&new_sigset, SIGINT);
+#ifndef NOSIGHUP
+    sigaddset(&new_sigset, SIGHUP);
+#endif /* NOSIGHUP */
+#ifdef SIGTSTP
+    sigaddset(&new_sigset, SIGTSTP);
+#endif /* SIGTSTP */
+#ifdef SIGWINCH
+    sigaddset(&new_sigset, SIGWINCH);
+#endif /* SIGWINCH */
+    sigprocmask(SIG_BLOCK, &new_sigset, &old_sigset);
+#endif /* HAVE_SIGACTION */
+
+    if ((fpid = fork()) == 0) {
+	/*
+	 * Child - for the long call.
+	 *
+	 * Make sure parent can kill us at will.  -BL
+	 */
+	(void) signal(SIGTERM, quench);
+
+	/*
+	 * Also make sure the child does not run one of the signal handlers
+	 * that may have been installed by Lynx if one of those signals
+	 * occurs.  For example we don't want the child to remove temp
+	 * files on ^C, let the parent deal with that.  - kw
+	 */
+	(void) signal(SIGINT, quench);
+#ifndef NOSIGHUP
+	(void) signal(SIGHUP, quench);
+#endif /* NOSIGHUP */
+#ifdef SIGTSTP
+	if (no_suspend)
+	    (void) signal(SIGTSTP, SIG_IGN);
+	else
+	    (void) signal(SIGTSTP, SIG_DFL);
+#endif /* SIGTSTP */
+#ifdef SIGWINCH
+	(void) signal(SIGWINCH, SIG_IGN);
+#endif /* SIGWINCH */
+#ifndef __linux__
+#ifndef DOSPATH
+	signal(SIGBUS, SIG_DFL);
+#endif /* DOSPATH */
+#endif /* !__linux__ */
+	signal(SIGSEGV, SIG_DFL);
+	signal(SIGILL, SIG_DFL);
+
+#if HAVE_SIGACTION
+	/* Restore signal mask to whatever it was before the fork. -kw */
+	sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+#endif /* HAVE_SIGACTION */
+
+	/*
+	 * Child won't use read side.  -BL
+	 */
+	close(pfd[0]);
+#ifdef HAVE_H_ERRNO
+	/* to detect cases when it doesn't get set although it should */
+	h_errno = -2;
+#endif
+	set_errno(0);
+	really(host, port, &statuses, rehostent);
+	/*
+	 * Send variables indicating status of lookup to parent.  That
+	 * includes rehostentlen, which the parent will use as the size for
+	 * the second read (if > 0).
+	 */
+	if (!statuses.child_errno)
+	    statuses.child_errno = errno;
+	IGNORE_RC(write(pfd[1], &statuses, sizeof(statuses)));
+
+	if (statuses.rehostentlen) {
+	    /*
+	     * Return our resulting rehostent through pipe...
+	     */
+	    IGNORE_RC(write(pfd[1], rehostent, statuses.rehostentlen));
+	    close(pfd[1]);
+	    _exit(0);
+	} else {
+	    /*
+	     * ...  or return error as exit code.
+	     */
+	    _exit(1);
+	}
+    }
+#if HAVE_SIGACTION
+    /*
+     * (parent) Restore signal mask to whatever it was before the fork.  -
+     * kw
+     */
+    sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+#endif /* HAVE_SIGACTION */
+
+    /*
+     * (parent) Wait until lookup finishes, or interrupt, or cycled too
+     * many times (just in case) -BL
+     */
+
+    close(pfd[1]);		/* parent won't use write side -BL */
+
+    if (fpid < 0) {		/* fork failed */
+	close(pfd[0]);
+	goto failed;
+    }
+
+    while (child_exited || (long) (time((time_t *) 0) - start_time) < dns_patience) {
+
+	FD_ZERO(&readfds);
+	/*
+	 * This allows us to abort immediately, not after 1-second timeout,
+	 * when user hits abort key.  Can't do this when using SLANG (or at
+	 * least I don't know how), so SLANG users must live with up-to-1s
+	 * timeout.  -BL
+	 *
+	 * Whoops -- we need to make sure stdin is actually selectable!
+	 * /dev/null isn't, on some systems, which makes some useful Lynx
+	 * invocations fail.  -BL
+	 */
+	{
+	    int kbd_fd = LYConsoleInputFD(TRUE);
+
+	    if (kbd_fd != INVSOC) {
+		FD_SET(kbd_fd, &readfds);
+	    }
+	}
+
+	one_second.tv_sec = 1;
+	one_second.tv_usec = 0;
+	FD_SET(pfd[0], &readfds);
+
+	/*
+	 * Return when data received, interrupted, or failed.  If nothing
+	 * is waiting, we sleep for 1 second in select(), to be nice to the
+	 * system.  -BL
+	 */
+#ifdef SOCKS
+	if (socks_flag)
+	    selret = Rselect(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
+	else
+#endif /* SOCKS */
+	    selret = select(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
+
+	if ((selret > 0) && FD_ISSET(pfd[0], &readfds)) {
+	    /*
+	     * First get status, including length of address.  -BL, kw
+	     */
+	    readret = read_bytes(pfd[0], (char *) &statuses, sizeof(statuses));
+	    if (readret == sizeof(statuses)) {
+		h_errno = statuses.child_h_errno;
+		set_errno(statuses.child_errno);
+#ifdef HAVE_H_ERRNO
+		if (statuses.h_errno_valid) {
+		    lynx_nsl_status = HT_H_ERRNO_VALID;
+		    /*
+		     * If something went wrong in the child process other
+		     * than normal lookup errors, and it appears that we
+		     * have enough info to know what went wrong, generate
+		     * diagnostic output.  ENOMEM observed on linux in
+		     * processes constrained with ulimit.  It would be too
+		     * unkind to abort the session, access to local files
+		     * or through a proxy may still work.  - kw
+		     */
+		    if (
+#ifdef NETDB_INTERNAL		/* linux glibc: defined in netdb.h */
+			   (errno && h_errno == NETDB_INTERNAL) ||
+#endif
+			   (errno == ENOMEM &&
+			    statuses.rehostentlen == 0 &&
+		    /* should probably be NETDB_INTERNAL if child
+		       memory exhausted, but we may find that
+		       h_errno remains unchanged. - kw */
+			    h_errno == -2)) {
+#ifndef MULTINET
+			HTInetStatus("CHILD gethostbyname");
+#endif
+			HTAlert(LYStrerror(statuses.child_errno));
+			if (errno == ENOMEM) {
+			    /*
+			     * Not much point in continuing, right?  Fake a
+			     * 'z', should shorten pointless guessing
+			     * cycle.  - kw
+			     */
+			    LYFakeZap(YES);
+			}
+		    }
+		}
+#endif /* HAVE_H_ERRNO */
+		if (statuses.rehostentlen > sizeof(LYNX_HOSTENT)) {
+		    /*
+		     * Then get the full reorganized hostent.  -BL, kw
+		     */
+		    readret = (*readit) (pfd[0], rehostent, statuses.rehostentlen);
+#ifdef DEBUG_HOSTENT
+		    dumpit("Read from pipe", rehostent);
+#endif
+		    if (readret == statuses.rehostentlen) {
+			got_rehostent = 1;
+			lynx_nsl_status = HT_OK;
+		    } else if (!statuses.h_errno_valid) {
+			lynx_nsl_status = HT_INTERNAL;
+		    }
+		}
+	    } else {
+		lynx_nsl_status = HT_ERROR;
+	    }
+	    /*
+	     * Make sure child is cleaned up.  -BL
+	     */
+	    if (!child_exited)
+		waitret = waitpid(fpid, &waitstat, WNOHANG);
+	    if (!WIFEXITED(waitstat) && !WIFSIGNALED(waitstat)) {
+		kill(fpid, SIGTERM);
+		waitret = waitpid(fpid, &waitstat, WNOHANG);
+	    }
+	    break;
+	}
+
+	/*
+	 * Clean up if child exited before & no data received.  -BL
+	 */
+	if (child_exited) {
+	    waitret = waitpid(fpid, &waitstat, WNOHANG);
+	    break;
+	}
+	/*
+	 * If child exited, loop once more looking for data.  -BL
+	 */
+	if ((waitret = waitpid(fpid, &waitstat, WNOHANG)) > 0) {
+	    /*
+	     * Data will be arriving right now, so make sure we don't
+	     * short-circuit out for too many loops, and skip the interrupt
+	     * check.  -BL
+	     */
+	    child_exited = 1;
+	    continue;
+	}
+
+	/*
+	 * Abort if interrupt key pressed.
+	 */
+	if (HTCheckForInterrupt()) {
+	    CTRACE((tfp, "LYGetHostByName: INTERRUPTED gethostbyname.\n"));
+	    kill(fpid, SIGTERM);
+	    waitpid(fpid, NULL, WNOHANG);
+	    close(pfd[0]);
+	    lynx_nsl_status = HT_INTERRUPTED;
+	    return FALSE;
+	}
+    }
+    close(pfd[0]);
+    if (waitret <= 0) {
+	kill(fpid, SIGTERM);
+	waitret = waitpid(fpid, &waitstat, WNOHANG);
+    }
+    if (waitret > 0) {
+	if (WIFEXITED(waitstat)) {
+	    CTRACE((tfp,
+		    "LYGetHostByName: NSL_FORK child %d exited, status 0x%x.\n",
+		    (int) waitret, WEXITSTATUS(waitstat)));
+	} else if (WIFSIGNALED(waitstat)) {
+	    CTRACE((tfp,
+		    "LYGetHostByName: NSL_FORK child %d got signal, status 0x%x!\n",
+		    (int) waitret, WTERMSIG(waitstat)));
+#ifdef WCOREDUMP
+	    if (WCOREDUMP(waitstat)) {
+		CTRACE((tfp,
+			"LYGetHostByName: NSL_FORK child %d dumped core!\n",
+			(int) waitret));
+	    }
+#endif /* WCOREDUMP */
+	} else if (WIFSTOPPED(waitstat)) {
+	    CTRACE((tfp,
+		    "LYGetHostByName: NSL_FORK child %d is stopped, status 0x%x!\n",
+		    (int) waitret, WSTOPSIG(waitstat)));
+	}
+    }
+    if (!got_rehostent) {
+	goto failed;
+    }
+    return TRUE;
+  failed:
+    return FALSE;
+}
+
+/*
+ * This is called via the child-side of the fork.
+ */
+static void really_gethostbyname(char *host,
+				 char *port GCC_UNUSED,
+				 STATUSES * statuses,
+				 void *rehostent)
+{
+    LYNX_HOSTENT *phost;	/* Pointer to host - See netdb.h */
+
+    (void) port;
+
+    phost = gethostbyname(host);
+    statuses->rehostentlen = 0;
+    statuses->child_errno = errno;
+    statuses->child_h_errno = h_errno;
+#ifdef HAVE_H_ERRNO
+    statuses->h_errno_valid = YES;
+#endif
+#ifdef MVS
+    CTRACE((tfp, "really_gethostbyname() returned %d\n", phost));
+#endif /* MVS */
+
+#ifdef DEBUG_HOSTENT_CHILD
+    dump_hostent("CHILD gethostbyname", phost);
+#endif
+    if (OK_HOST(phost)) {
+	statuses->rehostentlen = fill_rehostent(rehostent,
+						(size_t) REHOSTENT_SIZE,
+						phost);
+#ifdef DEBUG_HOSTENT_CHILD
+	dump_hostent("CHILD fill_rehostent", (LYNX_HOSTENT *) rehostent);
+#endif
+    }
+    if (statuses->rehostentlen <= sizeof(LYNX_HOSTENT) ||
+	!OK_HOST((LYNX_HOSTENT *) rehostent)) {
+	statuses->rehostentlen = 0;
+	statuses->h_length = 0;
+    } else {
+	statuses->h_length = ((LYNX_HOSTENT *) rehostent)->h_length;
+#ifdef HAVE_H_ERRNO
+	if (h_errno == -2)	/* success, but h_errno unchanged? */
+	    statuses->h_errno_valid = NO;
+#endif
+    }
+}
 #endif /* NSL_FORK */
 
 /*	Resolve an internet hostname, like gethostbyname
  *	------------------------------------------------
  *
  *  On entry,
- *	str	points to the given host name, not numeric address,
+ *	host	points to the given host name, not numeric address,
  *		without colon or port number.
  *
  *  On exit,
@@ -686,16 +1097,15 @@ static unsigned readit(int fd, char *buffer, size_t length)
  *	HT_ERROR		Resolver error, reason not known
  *	HT_INTERNAL		Internal error
  */
-LYNX_HOSTENT *LYGetHostByName(char *str)
+LYNX_HOSTENT *LYGetHostByName(char *host)
 {
-    char *host = str;
 
 #ifdef NSL_FORK
     /* for transfer of result between from child to parent: */
     static AlignedHOSTENT aligned_full_rehostent;
 
     /*
-     * We could define rehosten directly as a static char
+     * We could define rehostent directly as a static char
      * rehostent[REHOSTENT_SIZE], but the indirect approach via the above
      * struct should automatically take care of alignment requirements.
      * Note that, in addition,
@@ -707,17 +1117,6 @@ LYNX_HOSTENT *LYGetHostByName(char *str)
      *   call to fill_rehostent would be invalid when seen by the parent).  -kw
      */
     void *rehostent = (void *) &aligned_full_rehostent;
-
-    /* for transfer of status from child to parent: */
-    struct _statuses {
-	size_t rehostentlen;
-	int h_length;
-	int child_errno;	/* sometimes useful to pass this on */
-	int child_h_errno;
-	BOOL h_errno_valid;
-    } statuses;
-
-    size_t rehostentlen = 0;
 #endif /* NSL_FORK */
 
     LYNX_HOSTENT *result_phost = NULL;
@@ -726,16 +1125,16 @@ LYNX_HOSTENT *LYGetHostByName(char *str)
     _resolve_hook = ResolveYield;
 #endif
 
-    if (!str) {
+    if (!host) {
 	CTRACE((tfp, "LYGetHostByName: Can't parse `NULL'.\n"));
 	lynx_nsl_status = HT_INTERNAL;
 	return NULL;
     }
-    CTRACE((tfp, "LYGetHostByName: parsing `%s'.\n", str));
+    CTRACE((tfp, "LYGetHostByName: parsing `%s'.\n", host));
 
     /*  Could disable this if all our callers already check - kw */
     if (HTCheckForInterrupt()) {
-	CTRACE((tfp, "LYGetHostByName: INTERRUPTED for '%s'.\n", str));
+	CTRACE((tfp, "LYGetHostByName: INTERRUPTED for '%s'.\n", host));
 	lynx_nsl_status = HT_INTERRUPTED;
 	return NULL;
     }
@@ -760,381 +1159,13 @@ LYNX_HOSTENT *LYGetHostByName(char *str)
     lynx_nsl_status = HT_INTERNAL;	/* should be set to something else below */
 
 #ifdef NSL_FORK
-    statuses.h_errno_valid = NO;
-    /*
-     * Start block for fork-based gethostbyname() with checks for interrupts.
-     * - Tom Zerucha (tz@execpc.com) & FM
-     */
-    {
-	int got_rehostent = 0;
-
-#if HAVE_SIGACTION
-	sigset_t old_sigset;
-	sigset_t new_sigset;
-#endif
-	/*
-	 * Pipe, child pid, status buffers, start time, select() control
-	 * variables.
-	 */
-	int fpid, waitret;
-	int pfd[2], selret;
-	unsigned readret;
-
-#ifdef HAVE_TYPE_UNIONWAIT
-	union wait waitstat;
-
-#else
-	int waitstat = 0;
-#endif
-	time_t start_time = time((time_t *) 0);
-	fd_set readfds;
-	struct timeval one_second;
-	long dns_patience = 30;	/* how many seconds will we wait for DNS? */
-	int child_exited = 0;
-
-	/*
-	 * Reap any children that have terminated since last time through.
-	 * This might include children that we killed, then waited with WNOHANG
-	 * before they were actually ready to be reaped.  (Should be max of 1
-	 * in this state, but the loop is safe if waitpid() is implemented
-	 * correctly:  returns 0 when children exist but none have exited; -1
-	 * with errno == ECHILD when no children.) -BL
-	 */
-	do {
-	    waitret = waitpid(-1, 0, WNOHANG);
-	} while (waitret > 0 || (waitret == -1 && errno == EINTR));
-	waitret = 0;
-
-	IGNORE_RC(pipe(pfd));
-
-#if HAVE_SIGACTION
-	/*
-	 * Attempt to prevent a rare situation where the child could execute
-	 * the Lynx signal handlers because it gets killed before it even has a
-	 * chance to reset its handlers, resulting in bogus 'Exiting via
-	 * interrupt' message and screen corruption or worse.
-	 * Should that continue to be reported, for systems without
-	 * sigprocmask(), we need to find a different solutions for those.  -
-	 * kw 19990430
-	 */
-	sigemptyset(&new_sigset);
-	sigaddset(&new_sigset, SIGTERM);
-	sigaddset(&new_sigset, SIGINT);
-#ifndef NOSIGHUP
-	sigaddset(&new_sigset, SIGHUP);
-#endif /* NOSIGHUP */
-#ifdef SIGTSTP
-	sigaddset(&new_sigset, SIGTSTP);
-#endif /* SIGTSTP */
-#ifdef SIGWINCH
-	sigaddset(&new_sigset, SIGWINCH);
-#endif /* SIGWINCH */
-	sigprocmask(SIG_BLOCK, &new_sigset, &old_sigset);
-#endif /* HAVE_SIGACTION */
-
-	if ((fpid = fork()) == 0) {
-	    LYNX_HOSTENT *phost;	/* Pointer to host - See netdb.h */
-
-	    /*
-	     * Child - for the long call.
-	     *
-	     * Make sure parent can kill us at will.  -BL
-	     */
-	    (void) signal(SIGTERM, quench);
-
-	    /*
-	     * Also make sure the child does not run one of the signal handlers
-	     * that may have been installed by Lynx if one of those signals
-	     * occurs.  For example we don't want the child to remove temp
-	     * files on ^C, let the parent deal with that.  - kw
-	     */
-	    (void) signal(SIGINT, quench);
-#ifndef NOSIGHUP
-	    (void) signal(SIGHUP, quench);
-#endif /* NOSIGHUP */
-#ifdef SIGTSTP
-	    if (no_suspend)
-		(void) signal(SIGTSTP, SIG_IGN);
-	    else
-		(void) signal(SIGTSTP, SIG_DFL);
-#endif /* SIGTSTP */
-#ifdef SIGWINCH
-	    (void) signal(SIGWINCH, SIG_IGN);
-#endif /* SIGWINCH */
-#ifndef __linux__
-#ifndef DOSPATH
-	    signal(SIGBUS, SIG_DFL);
-#endif /* DOSPATH */
-#endif /* !__linux__ */
-	    signal(SIGSEGV, SIG_DFL);
-	    signal(SIGILL, SIG_DFL);
-
-#if HAVE_SIGACTION
-	    /* Restore signal mask to whatever it was before the fork. -kw */
-	    sigprocmask(SIG_SETMASK, &old_sigset, NULL);
-#endif /* HAVE_SIGACTION */
-
-	    /*
-	     * Child won't use read side.  -BL
-	     */
-	    close(pfd[0]);
-#ifdef HAVE_H_ERRNO
-	    /* to detect cases when it doesn't get set although it should */
-	    h_errno = -2;
-#endif
-	    set_errno(0);
-	    phost = gethostbyname(host);
-	    statuses.child_errno = errno;
-	    statuses.child_h_errno = h_errno;
-#ifdef HAVE_H_ERRNO
-	    statuses.h_errno_valid = YES;
-#endif
-#ifdef MVS
-	    CTRACE((tfp, "LYGetHostByName: gethostbyname() returned %d\n", phost));
-#endif /* MVS */
-
-#ifdef DEBUG_HOSTENT_CHILD
-	    dump_hostent("CHILD gethostbyname", phost);
-#endif
-	    if (OK_HOST(phost)) {
-		rehostentlen = fill_rehostent(rehostent,
-					      (size_t) REHOSTENT_SIZE,
-					      phost);
-#ifdef DEBUG_HOSTENT_CHILD
-		dump_hostent("CHILD fill_rehostent", (LYNX_HOSTENT *) rehostent);
-#endif
-	    }
-	    if (rehostentlen <= sizeof(LYNX_HOSTENT) ||
-		!OK_HOST((LYNX_HOSTENT *) rehostent)) {
-		rehostentlen = 0;
-		statuses.h_length = 0;
-	    } else {
-		statuses.h_length = ((LYNX_HOSTENT *) rehostent)->h_length;
-#ifdef HAVE_H_ERRNO
-		if (h_errno == -2)	/* success, but h_errno unchanged? */
-		    statuses.h_errno_valid = NO;
-#endif
-	    }
-	    /*
-	     * Send variables indicating status of lookup to parent.  That
-	     * includes rehostentlen, which the parent will use as the size for
-	     * the second read (if > 0).
-	     */
-	    if (!statuses.child_errno)
-		statuses.child_errno = errno;
-	    statuses.rehostentlen = rehostentlen;
-	    IGNORE_RC(write(pfd[1], &statuses, sizeof(statuses)));
-
-	    if (rehostentlen) {
-		/*
-		 * Return our resulting rehostent through pipe...
-		 */
-		IGNORE_RC(write(pfd[1], rehostent, rehostentlen));
-		close(pfd[1]);
-		_exit(0);
-	    } else {
-		/*
-		 * ...  or return error as exit code.
-		 */
-		_exit(1);
-	    }
-	}
-#if HAVE_SIGACTION
-	/*
-	 * (parent) Restore signal mask to whatever it was before the fork.  -
-	 * kw
-	 */
-	sigprocmask(SIG_SETMASK, &old_sigset, NULL);
-#endif /* HAVE_SIGACTION */
-
-	/*
-	 * (parent) Wait until lookup finishes, or interrupt, or cycled too
-	 * many times (just in case) -BL
-	 */
-
-	close(pfd[1]);		/* parent won't use write side -BL */
-
-	if (fpid < 0) {		/* fork failed */
-	    close(pfd[0]);
-	    goto failed;
-	}
-
-	while (child_exited || (long) (time((time_t *) 0) - start_time) < dns_patience) {
-
-	    FD_ZERO(&readfds);
-	    /*
-	     * This allows us to abort immediately, not after 1-second timeout,
-	     * when user hits abort key.  Can't do this when using SLANG (or at
-	     * least I don't know how), so SLANG users must live with up-to-1s
-	     * timeout.  -BL
-	     *
-	     * Whoops -- we need to make sure stdin is actually selectable!
-	     * /dev/null isn't, on some systems, which makes some useful Lynx
-	     * invocations fail.  -BL
-	     */
-	    {
-		int kbd_fd = LYConsoleInputFD(TRUE);
-
-		if (kbd_fd != INVSOC) {
-		    FD_SET(kbd_fd, &readfds);
-		}
-	    }
-
-	    one_second.tv_sec = 1;
-	    one_second.tv_usec = 0;
-	    FD_SET(pfd[0], &readfds);
-
-	    /*
-	     * Return when data received, interrupted, or failed.  If nothing
-	     * is waiting, we sleep for 1 second in select(), to be nice to the
-	     * system.  -BL
-	     */
-#ifdef SOCKS
-	    if (socks_flag)
-		selret = Rselect(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
-	    else
-#endif /* SOCKS */
-		selret = select(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
-
-	    if ((selret > 0) && FD_ISSET(pfd[0], &readfds)) {
-		/*
-		 * First get status, including length of address.  -BL, kw
-		 */
-		readret = readit(pfd[0], (char *) &statuses, sizeof(statuses));
-		if (readret == sizeof(statuses)) {
-		    h_errno = statuses.child_h_errno;
-		    set_errno(statuses.child_errno);
-#ifdef HAVE_H_ERRNO
-		    if (statuses.h_errno_valid) {
-			lynx_nsl_status = HT_H_ERRNO_VALID;
-			/*
-			 * If something went wrong in the child process other
-			 * than normal lookup errors, and it appears that we
-			 * have enough info to know what went wrong, generate
-			 * diagnostic output.  ENOMEM observed on linux in
-			 * processes constrained with ulimit.  It would be too
-			 * unkind to abort the session, access to local files
-			 * or through a proxy may still work.  - kw
-			 */
-			if (
-#ifdef NETDB_INTERNAL		/* linux glibc: defined in netdb.h */
-			       (errno && h_errno == NETDB_INTERNAL) ||
-#endif
-			       (errno == ENOMEM &&
-				statuses.rehostentlen == 0 &&
-			/* should probably be NETDB_INTERNAL if child
-			   memory exhausted, but we may find that
-			   h_errno remains unchanged. - kw */
-				h_errno == -2)) {
-#ifndef MULTINET
-			    HTInetStatus("CHILD gethostbyname");
-#endif
-			    HTAlert(LYStrerror(statuses.child_errno));
-			    if (errno == ENOMEM) {
-				/*
-				 * Not much point in continuing, right?  Fake a
-				 * 'z', should shorten pointless guessing
-				 * cycle.  - kw
-				 */
-				LYFakeZap(YES);
-			    }
-			}
-		    }
-#endif /* HAVE_H_ERRNO */
-		    if (statuses.rehostentlen > sizeof(LYNX_HOSTENT)) {
-			/*
-			 * Then get the full reorganized hostent.  -BL, kw
-			 */
-			readret = readit(pfd[0], rehostent, statuses.rehostentlen);
-#ifdef DEBUG_HOSTENT
-			dump_hostent("Read from pipe", (LYNX_HOSTENT *) rehostent);
-#endif
-			if (readret == statuses.rehostentlen) {
-			    got_rehostent = 1;
-			    result_phost = (LYNX_HOSTENT *) rehostent;
-			    lynx_nsl_status = HT_OK;
-			} else if (!statuses.h_errno_valid) {
-			    lynx_nsl_status = HT_INTERNAL;
-			}
-		    }
-		} else {
-		    lynx_nsl_status = HT_ERROR;
-		}
-		/*
-		 * Make sure child is cleaned up.  -BL
-		 */
-		if (!child_exited)
-		    waitret = waitpid(fpid, &waitstat, WNOHANG);
-		if (!WIFEXITED(waitstat) && !WIFSIGNALED(waitstat)) {
-		    kill(fpid, SIGTERM);
-		    waitret = waitpid(fpid, &waitstat, WNOHANG);
-		}
-		break;
-	    }
-
-	    /*
-	     * Clean up if child exited before & no data received.  -BL
-	     */
-	    if (child_exited) {
-		waitret = waitpid(fpid, &waitstat, WNOHANG);
-		break;
-	    }
-	    /*
-	     * If child exited, loop once more looking for data.  -BL
-	     */
-	    if ((waitret = waitpid(fpid, &waitstat, WNOHANG)) > 0) {
-		/*
-		 * Data will be arriving right now, so make sure we don't
-		 * short-circuit out for too many loops, and skip the interrupt
-		 * check.  -BL
-		 */
-		child_exited = 1;
-		continue;
-	    }
-
-	    /*
-	     * Abort if interrupt key pressed.
-	     */
-	    if (HTCheckForInterrupt()) {
-		CTRACE((tfp, "LYGetHostByName: INTERRUPTED gethostbyname.\n"));
-		kill(fpid, SIGTERM);
-		waitpid(fpid, NULL, WNOHANG);
-		close(pfd[0]);
-		lynx_nsl_status = HT_INTERRUPTED;
-		return NULL;
-	    }
-	}
-	close(pfd[0]);
-	if (waitret <= 0) {
-	    kill(fpid, SIGTERM);
-	    waitret = waitpid(fpid, &waitstat, WNOHANG);
-	}
-	if (waitret > 0) {
-	    if (WIFEXITED(waitstat)) {
-		CTRACE((tfp,
-			"LYGetHostByName: NSL_FORK child %d exited, status 0x%x.\n",
-			(int) waitret, WEXITSTATUS(waitstat)));
-	    } else if (WIFSIGNALED(waitstat)) {
-		CTRACE((tfp,
-			"LYGetHostByName: NSL_FORK child %d got signal, status 0x%x!\n",
-			(int) waitret, WTERMSIG(waitstat)));
-#ifdef WCOREDUMP
-		if (WCOREDUMP(waitstat)) {
-		    CTRACE((tfp,
-			    "LYGetHostByName: NSL_FORK child %d dumped core!\n",
-			    (int) waitret));
-		}
-#endif /* WCOREDUMP */
-	    } else if (WIFSTOPPED(waitstat)) {
-		CTRACE((tfp,
-			"LYGetHostByName: NSL_FORK child %d is stopped, status 0x%x!\n",
-			(int) waitret, WSTOPSIG(waitstat)));
-	    }
-	}
-	if (!got_rehostent) {
-	    goto failed;
-	}
+    if (!setup_nsl_fork(really_gethostbyname,
+			read_bytes,
+			dump_hostent,
+			host, NULL, rehostent)) {
+	goto failed;
     }
+    result_phost = (LYNX_HOSTENT *) rehostent;
 #else /* Not NSL_FORK: */
 
 #ifdef _WINDOWS_NSL
@@ -1394,11 +1425,151 @@ static int HTParseInet(SockA * soc_in, const char *str)
 #endif /* !INET6 */
 
 #ifdef INET6
+
+#if defined(NSL_FORK)
+
+#define MAX_ADDRINFO 6
+
+typedef struct {
+    LYNX_ADDRINFO h[MAX_ADDRINFO];
+    char heap[128 * MAX_ADDRINFO];
+} AlignedADDRINFO;
+
+#ifdef DEBUG_HOSTENT_CHILD
+static void dump_addrinfo(const char *tag, void *data)
+{
+    LYNX_ADDRINFO *res;
+
+    for (res = (LYNX_ADDRINFO *) data; res; res = res->ai_next) {
+	CTRACE((tfp, "%s: family %d, socktype %d, protocol %d\n",
+		tag,
+		res->ai_family,
+		res->ai_socktype,
+		res->ai_protocol));
+    }
+}
+#endif
+
+/*
+ * Copy the relevant information.
+ */
+static size_t fill_addrinfo(char *buffer,
+			    const LYNX_ADDRINFO *phost)
+{
+    LYNX_ADDRINFO *actual = (LYNX_ADDRINFO *) buffer;
+    int count = 0;
+    char *heap = (char *) &(actual[MAX_ADDRINFO]);
+
+    while (count++ < MAX_ADDRINFO) {
+	memset(actual, 0, sizeof(LYNX_ADDRINFO));
+
+	actual->ai_flags = phost->ai_flags;
+	actual->ai_family = phost->ai_family;
+	actual->ai_socktype = phost->ai_socktype;
+	actual->ai_protocol = phost->ai_protocol;
+	actual->ai_addrlen = phost->ai_addrlen;
+
+	if ((int) (heap + phost->ai_addrlen - buffer) >=
+	    (int) sizeof(AlignedADDRINFO)) {
+	    heap = buffer;
+	    break;
+	}
+
+	actual->ai_addr = (struct sockaddr *) heap;
+	MemCpy(heap, phost->ai_addr, phost->ai_addrlen);
+	heap += phost->ai_addrlen;
+
+	phost = phost->ai_next;
+	if (phost != 0) {
+	    actual->ai_next = (actual + 1);
+	    ++actual;
+	} else {
+	    break;
+	}
+    }
+    return (size_t) (heap - buffer);
+}
+
+/*
+ * Read data, repair pointers as done in fill_addrinfo().
+ */
+static unsigned read_addrinfo(int fd, char *buffer, size_t length)
+{
+    unsigned result = read_bytes(fd, buffer, length);
+    LYNX_ADDRINFO *actual = (LYNX_ADDRINFO *) buffer;
+    int count = 0;
+    char *heap = (char *) &(actual[MAX_ADDRINFO]);
+
+    while (count++ < MAX_ADDRINFO) {
+	if (actual->ai_addr) {
+	    actual->ai_addr = (struct sockaddr *) heap;
+	    heap += actual->ai_addrlen;
+	}
+	if (actual->ai_next == 0)
+	    break;
+	actual->ai_next = (actual + 1);
+	++actual;
+    }
+
+    return result;
+}
+
+/*
+ * This is called via the child-side of the fork.
+ */
+static void really_getaddrinfo(char *host,
+			       char *port,
+			       STATUSES * statuses,
+			       void *result)
+{
+    LYNX_ADDRINFO hints, *res;
+    int error;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    error = getaddrinfo(host, port, &hints, &res);
+    if (error || !res) {
+	CTRACE((tfp, "HTGetAddrInfo: getaddrinfo(%s, %s): %s\n", host, port,
+		gai_strerror(error)));
+    } else {
+	statuses->child_errno = errno;
+	statuses->child_h_errno = h_errno;
+#ifdef HAVE_H_ERRNO
+	statuses->h_errno_valid = YES;
+#endif
+
+#ifdef DEBUG_HOSTENT_CHILD
+	dump_addrinfo("CHILD getaddrinfo", res);
+#endif
+	statuses->rehostentlen = fill_addrinfo(result, res);
+#ifdef DEBUG_HOSTENT_CHILD
+	dump_addrinfo("CHILD fill_readdrinfo", (LYNX_ADDRINFO *) result);
+#endif
+	if (statuses->rehostentlen <= sizeof(LYNX_ADDRINFO)) {
+	    statuses->rehostentlen = 0;
+	    statuses->h_length = 0;
+	} else {
+	    statuses->h_length = (int) (((LYNX_ADDRINFO *) result)->ai_addrlen);
+	}
+    }
+}
+#endif /* NSL_FORK */
+
 static LYNX_ADDRINFO *HTGetAddrInfo(const char *str,
 				    const int defport)
 {
-    LYNX_ADDRINFO hints, *res;
+#ifdef NSL_FORK
+    /* for transfer of result between from child to parent: */
+    static AlignedADDRINFO aligned_full_readdrinfo;
+
+    void *readdrinfo = (void *) &aligned_full_readdrinfo;
+
+#else
+    LYNX_ADDRINFO hints;
     int error;
+#endif /* NSL_FORK */
+    LYNX_ADDRINFO *res;
     char *p;
     char *s = NULL;
     char *host, *port;
@@ -1421,6 +1592,16 @@ static LYNX_ADDRINFO *HTGetAddrInfo(const char *str,
 	port = pbuf;
     }
 
+#ifdef NSL_FORK
+    if (setup_nsl_fork(really_getaddrinfo,
+		       read_addrinfo,
+		       dump_addrinfo,
+		       host, port, readdrinfo)) {
+	res = readdrinfo;
+    } else {
+	res = NULL;
+    }
+#else
     memset(&hints, 0, sizeof(hints));
     hints.ai_family = PF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
@@ -1430,6 +1611,7 @@ static LYNX_ADDRINFO *HTGetAddrInfo(const char *str,
 		gai_strerror(error)));
 	res = NULL;
     }
+#endif
 
     free(s);
     return res;
@@ -1768,8 +1950,10 @@ int HTDoConnect(const char *url,
 		    HTAlert(gettext("Connection failed (too many retries)."));
 #ifdef INET6
 		    FREE(line);
+#ifndef NSL_FORK
 		    if (res0)
 			freeaddrinfo(res0);
+#endif
 #endif /* INET6 */
 		    return HT_NO_DATA;
 		}
@@ -1959,8 +2143,10 @@ int HTDoConnect(const char *url,
 
 #ifdef INET6
     FREE(line);
+#ifndef NSL_FORK
     if (res0)
 	freeaddrinfo(res0);
+#endif
 #endif /* INET6 */
     return status;
 }