about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation/HTTCP.c
diff options
context:
space:
mode:
Diffstat (limited to 'WWW/Library/Implementation/HTTCP.c')
-rw-r--r--WWW/Library/Implementation/HTTCP.c2182
1 files changed, 2182 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c
new file mode 100644
index 00000000..2723422c
--- /dev/null
+++ b/WWW/Library/Implementation/HTTCP.c
@@ -0,0 +1,2182 @@
+/*
+ * $LynxId: HTTCP.c,v 1.107 2012/02/09 12:36:45 tom Exp $
+ *
+ *			Generic Communication Code		HTTCP.c
+ *			==========================
+ *
+ *	This code is in common between client and server sides.
+ *
+ *	16 Jan 92  TBL	Fix strtol() undefined on CMU Mach.
+ *	25 Jun 92  JFG	Added DECNET option through TCP socket emulation.
+ *	13 Sep 93  MD	Added correct return of vmserrorno for HTInetStatus.
+ *			Added decoding of vms error message for MULTINET.
+ *	7-DEC-1993 Bjorn S. Nilsson, ALEPH, CERN, VMS UCX ioctl() changes
+ *			(done of Mosaic)
+ *	19 Feb 94  Danny Mayer	Added Bjorn Fixes to Lynx version
+ *	 7 Mar 94  Danny Mayer	Added Fix UCX version for full domain name
+ *	20 May 94  Andy Harper	Added support for CMU TCP/IP transport
+ *	17 Nov 94  Andy Harper	Added support for SOCKETSHR transport
+ *	16 Jul 95  S. Bjorndahl added kluge to deal with LIBCMU bug
+ */
+
+#include <HTUtils.h>
+#include <HTParse.h>
+#include <HTAlert.h>
+#include <HTTCP.h>
+#include <LYGlobalDefs.h>	/* added for no_suspend */
+#include <LYUtils.h>
+
+#ifdef NSL_FORK
+#include <signal.h>
+#include <www_wait.h>
+#endif /* NSL_FORK */
+
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+#ifdef __DJGPP__
+#include <netdb.h>
+#endif /* __DJGPP__ */
+
+#define LYNX_ADDRINFO	struct addrinfo
+#define LYNX_HOSTENT	struct hostent
+
+#define OK_HOST(p) ((p) != 0 && ((p)->h_length) != 0)
+
+#ifdef SVR4_BSDSELECT
+int BSDselect(int nfds,
+	      fd_set * readfds,
+	      fd_set * writefds,
+	      fd_set * exceptfds,
+	      struct timeval *select_timeout);
+
+#ifdef select
+#undef select
+#endif /* select */
+#define select BSDselect
+#ifdef SOCKS
+#ifdef Rselect
+#undef Rselect
+#endif /* Rselect */
+#define Rselect BSDselect
+#endif /* SOCKS */
+#endif /* SVR4_BSDSELECT */
+
+#include <LYLeaks.h>
+
+/*
+ *  Module-Wide variables
+ */
+static char *hostname = NULL;	/* The name of this host */
+
+/*
+ *  PUBLIC VARIABLES
+ */
+#ifdef SOCKS
+unsigned long socks_bind_remoteAddr;	/* for long Rbind */
+#endif /* SOCKS */
+
+/*	Encode INET status (as in sys/errno.h)			  inet_status()
+ *	------------------
+ *
+ *  On entry,
+ *	where		gives a description of what caused the error
+ *	global errno	gives the error number in the Unix way.
+ *
+ *  On return,
+ *	returns		a negative status in the Unix way.
+ */
+
+#ifdef DECL_SYS_ERRLIST
+extern char *sys_errlist[];	/* see man perror on cernvax */
+extern int sys_nerr;
+#endif /* DECL_SYS_ERRLIST */
+
+#ifdef __DJGPP__
+static int ResolveYield(void)
+{
+    return HTCheckForInterrupt()? 0 : 1;
+}
+#endif
+
+#if defined(VMS) && defined(UCX)
+/*
+ *  A routine to mimic the ioctl function for UCX.
+ *  Bjorn S. Nilsson, 25-Nov-1993. Based on an example in the UCX manual.
+ */
+#include <HTioctl.h>
+
+int HTioctl(int d,
+	    int request,
+	    int *argp)
+{
+    int sdc, status;
+    unsigned short fun, iosb[4];
+    char *p5, *p6;
+    struct comm {
+	int command;
+	char *addr;
+    } ioctl_comm;
+    struct it2 {
+	unsigned short len;
+	unsigned short opt;
+	struct comm *addr;
+    } ioctl_desc;
+
+    if ((sdc = vaxc$get_sdc(d)) == 0) {
+	set_errno(EBADF);
+	return -1;
+    }
+    ioctl_desc.opt = UCX$C_IOCTL;
+    ioctl_desc.len = sizeof(struct comm);
+
+    ioctl_desc.addr = &ioctl_comm;
+    if (request & IOC_OUT) {
+	fun = IO$_SENSEMODE;
+	p5 = 0;
+	p6 = (char *) &ioctl_desc;
+    } else {
+	fun = IO$_SETMODE;
+	p5 = (char *) &ioctl_desc;
+	p6 = 0;
+    }
+    ioctl_comm.command = request;
+    ioctl_comm.addr = (char *) argp;
+    status = sys$qiow(0, sdc, fun, iosb, 0, 0, 0, 0, 0, 0, p5, p6);
+    if (!(status & 01)) {
+	set_errno(status);
+	return -1;
+    }
+    if (!(iosb[0] & 01)) {
+	set_errno(iosb[0]);
+	return -1;
+    }
+    return 0;
+}
+#endif /* VMS && UCX */
+
+#define MY_FORMAT "TCP: Error %d in `SOCKET_ERRNO' after call to %s() failed.\n\t%s\n"
+	   /* third arg is transport/platform specific */
+
+/*	Report Internet Error
+ *	---------------------
+ */
+int HTInetStatus(const char *where)
+{
+    int status;
+    int saved_errno = errno;
+
+#ifdef VMS
+#ifdef MULTINET
+    SOCKET_ERRNO = vmserrno;
+#endif /* MULTINET */
+#endif /* VMS */
+
+#ifdef VM
+    CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+	    "(Error number not translated)"));	/* What Is the VM equiv? */
+#define ER_NO_TRANS_DONE
+#endif /* VM */
+
+#ifdef VMS
+#ifdef MULTINET
+    CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+	    vms_errno_string()));
+#else
+    CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+	    ((SOCKET_ERRNO > 0 && SOCKET_ERRNO <= 65) ?
+	     strerror(SOCKET_ERRNO) : "(Error number not translated)")));
+#endif /* MULTINET */
+#define ER_NO_TRANS_DONE
+#endif /* VMS */
+
+#ifdef HAVE_STRERROR
+    CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+	    strerror(SOCKET_ERRNO)));
+#define ER_NO_TRANS_DONE
+#endif /* HAVE_STRERROR */
+
+#ifndef ER_NO_TRANS_DONE
+    CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
+	    (SOCKET_ERRNO < sys_nerr ?
+	     sys_errlist[SOCKET_ERRNO] : "Unknown error")));
+#endif /* !ER_NO_TRANS_DONE */
+
+#ifdef VMS
+#ifndef MULTINET
+    CTRACE((tfp,
+	    "         Unix error number (SOCKET_ERRNO) = %ld dec\n",
+	    SOCKET_ERRNO));
+    CTRACE((tfp,
+	    "         VMS error (vaxc$errno)    = %lx hex\n",
+	    vaxc$errno));
+#endif /* MULTINET */
+#endif /* VMS */
+
+    set_errno(saved_errno);
+
+#ifdef VMS
+    /*
+     * uerrno and errno happen to be zero if vmserrno <> 0
+     */
+#ifdef MULTINET
+    status = -vmserrno;
+#else
+    status = -vaxc$errno;
+#endif /* MULTINET */
+#else
+    status = -SOCKET_ERRNO;
+#endif /* VMS */
+    return status;
+}
+
+/*	Parse a cardinal value				       parse_cardinal()
+ *	----------------------
+ *
+ * On entry,
+ *	*pp	    points to first character to be interpreted, terminated by
+ *		    non 0:9 character.
+ *	*pstatus    points to status already valid
+ *	maxvalue    gives the largest allowable value.
+ *
+ * On exit,
+ *	*pp	    points to first unread character
+ *	*pstatus    points to status updated iff bad
+ */
+unsigned int HTCardinal(int *pstatus,
+			char **pp,
+			unsigned int max_value)
+{
+    unsigned int n;
+
+    if ((**pp < '0') || (**pp > '9')) {		/* Null string is error */
+	*pstatus = -3;		/* No number where one expected */
+	return 0;
+    }
+
+    n = 0;
+    while ((**pp >= '0') && (**pp <= '9'))
+	n = n * 10 + (unsigned) (*((*pp)++) - '0');
+
+    if (n > max_value) {
+	*pstatus = -4;		/* Cardinal outside range */
+	return 0;
+    }
+
+    return n;
+}
+
+#ifndef DECNET			/* Function only used below for a trace message */
+/*	Produce a string for an Internet address
+ *	----------------------------------------
+ *
+ *  On exit,
+ *	returns a pointer to a static string which must be copied if
+ *		it is to be kept.
+ */
+const char *HTInetString(SockA * soc_in)
+{
+#ifdef INET6
+    static char hostbuf[MAXHOSTNAMELEN];
+
+    getnameinfo((struct sockaddr *) soc_in,
+		SOCKADDR_LEN(soc_in),
+		hostbuf, (socklen_t) sizeof(hostbuf),
+		NULL, 0,
+		NI_NUMERICHOST);
+    return hostbuf;
+#else
+    static char string[20];
+
+    sprintf(string, "%d.%d.%d.%d",
+	    (int) *((unsigned char *) (&soc_in->sin_addr) + 0),
+	    (int) *((unsigned char *) (&soc_in->sin_addr) + 1),
+	    (int) *((unsigned char *) (&soc_in->sin_addr) + 2),
+	    (int) *((unsigned char *) (&soc_in->sin_addr) + 3));
+    return string;
+#endif /* INET6 */
+}
+#endif /* !DECNET */
+
+/*	Check whether string is a valid Internet hostname - kw
+ *	-------------------------------------------------
+ *
+ *  Checks whether
+ *  - contains only valid chars for domain names (actually, the
+ *    restrictions are somewhat relaxed),
+ *  - no leading dots or empty segments,
+ *  - no segment starts with '-' or '+' [this protects telnet command],
+ *  - max. length of dot-separated segment <= 63 (RFC 1034,1035),
+ *  - total length <= 254 (if it ends with dot) or 253 (otherwise)
+ *     [an interpretation of RFC 1034,1035, although RFC 1123
+ *      suggests 255 as limit - kw].
+ *
+ *  Note: user (before '@') and port (after ':') components from
+ *      host part of URL should be already stripped (if appropriate)
+ *      from the input string.
+ *
+ *  On exit,
+ *	returns 1 if valid, otherwise 0.
+ */
+BOOL valid_hostname(char *name)
+{
+    int i = 1, iseg = 0;
+    char *cp = name;
+
+    if (!(name && *name))
+	return NO;
+    for (; (*cp && i <= 253); cp++, i++) {
+	if (*cp == '.') {
+	    if (iseg == 0) {
+		return NO;
+	    } else {
+		iseg = 0;
+		continue;
+	    }
+	} else if (iseg == 0 && (*cp == '-' || *cp == '+')) {
+	    return NO;
+	} else if (++iseg > 63) {
+	    return NO;
+	}
+	if (!isalnum(UCH(*cp)) &&
+	    *cp != '-' && *cp != '_' &&
+	    *cp != '$' && *cp != '+') {
+	    return NO;
+	}
+    }
+    return (BOOL) (*cp == '\0' || (*cp == '.' && iseg != 0 && cp[1] == '\0'));
+}
+
+#ifdef NSL_FORK
+/*
+ *  Function to allow us to be killed with a normal signal (not
+ *  SIGKILL), but don't go through normal libc exit() processing, which
+ *  would screw up parent's stdio.  -BL
+ */
+static void quench(int sig GCC_UNUSED)
+{
+    _exit(2);
+}
+#endif /* NSL_FORK */
+
+int lynx_nsl_status = HT_OK;
+
+#define DEBUG_HOSTENT		/* disable in case of problems */
+#define DEBUG_HOSTENT_CHILD	/* for NSL_FORK, may screw up trace file */
+
+/*
+ *  Two auxiliary functions for name lookup and LYNX_HOSTENT.
+ *
+ *  dump_hostent - dumps the contents of a LYNX_HOSTENT to the
+ *  trace log or stderr, including all pointer values, strings, and
+ *  addresses, in a format inspired by gdb's print format. - kw
+ */
+static void dump_hostent(const char *msgprefix,
+			 const LYNX_HOSTENT *phost)
+{
+    if (TRACE) {
+	int i;
+	char **pcnt;
+
+	CTRACE((tfp, "%s: %p ", msgprefix, (const void *) phost));
+	if (phost) {
+	    CTRACE((tfp, "{ h_name = %p", phost->h_name));
+	    if (phost->h_name) {
+		CTRACE((tfp, " \"%s\",", phost->h_name));
+	    } else {
+		CTRACE((tfp, ","));
+	    }
+	    CTRACE((tfp, "\n\t h_aliases = %p", (void *) phost->h_aliases));
+	    if (phost->h_aliases) {
+		CTRACE((tfp, " {"));
+		for (pcnt = phost->h_aliases; *pcnt; pcnt++) {
+		    CTRACE((tfp, "%s %p \"%s\"",
+			    (pcnt == phost->h_aliases ? " " : ", "),
+			    *pcnt, *pcnt));
+		}
+		CTRACE((tfp, "%s0x0 },\n\t",
+			(*phost->h_aliases ? ", " : " ")));
+	    } else {
+		CTRACE((tfp, ",\n\t"));
+	    }
+	    CTRACE((tfp, " h_addrtype = %d,", phost->h_addrtype));
+	    CTRACE((tfp, " h_length = %d,\n\t", phost->h_length));
+	    CTRACE((tfp, " h_addr_list = %p", (void *) phost->h_addr_list));
+	    if (phost->h_addr_list) {
+		CTRACE((tfp, " {"));
+		for (pcnt = phost->h_addr_list; *pcnt; pcnt++) {
+		    CTRACE((tfp, "%s %p",
+			    (pcnt == phost->h_addr_list ? "" : ","),
+			    *pcnt));
+		    for (i = 0; i < phost->h_length; i++) {
+			CTRACE((tfp, "%s%d%s", (i == 0 ? " \"" : "."),
+				(int) *((unsigned char *) (*pcnt) + i),
+				(i + 1 == phost->h_length ? "\"" : "")));
+		    }
+		}
+		if (*phost->h_addr_list) {
+		    CTRACE((tfp, ", 0x0 } }"));
+		} else {
+		    CTRACE((tfp, " 0x0 } }"));
+		}
+	    } else {
+		CTRACE((tfp, "}"));
+	    }
+	}
+	CTRACE((tfp, "\n"));
+	fflush(tfp);
+    }
+}
+
+/*
+ *  fill_rehostent - copies as much as possible relevant content from
+ *  the LYNX_HOSTENT pointed to by phost to the char buffer given
+ *  by rehostent, subject to maximum output length rehostentsize,
+ *  following pointers and building self-contained output which can be
+ *  cast to a LYNX_HOSTENT. - kw
+ *  See also description of LYGetHostByName.
+ */
+#if defined(NSL_FORK) || defined(_WINDOWS_NSL)
+
+#define REHOSTENT_SIZE 128	/* not bigger than pipe buffer! */
+
+typedef struct {
+    LYNX_HOSTENT h;
+    char rest[REHOSTENT_SIZE];
+} AlignedHOSTENT;
+
+static size_t fill_rehostent(char *rehostent,
+			     size_t rehostentsize,
+			     const LYNX_HOSTENT *phost)
+{
+    AlignedHOSTENT *data = (AlignedHOSTENT *) (void *) rehostent;
+    int num_addrs = 0;
+    int num_aliases = 0;
+    char **pcnt;
+    char *p_next_char;
+    char **p_next_charptr;
+    size_t name_len = 0;
+    size_t required_per_addr;
+    size_t curlen = sizeof(LYNX_HOSTENT);
+    size_t available = rehostentsize - curlen;
+    size_t chk_available, mem_this_alias, required_this_alias;
+    int i_addr, i_alias;
+
+    if (!phost)
+	return 0;
+    required_per_addr = (size_t) phost->h_length + sizeof(char *);
+
+    if (phost->h_addr_list)
+	available -= sizeof(phost->h_addr_list[0]);
+    if (phost->h_aliases)
+	available -= sizeof(phost->h_aliases[0]);
+    if (phost->h_name)
+	available--;
+    if (phost->h_addr_list) {
+	if (phost->h_addr_list[0]) {
+	    if (available >= required_per_addr) {
+		num_addrs++;
+		available -= required_per_addr;
+	    }
+	}
+    }
+    if (phost->h_name) {
+	name_len = strlen(phost->h_name);
+	if (available >= name_len) {
+	    available -= name_len;
+	} else {
+	    name_len = 0;
+	}
+    }
+    if (num_addrs) {
+	for (pcnt = phost->h_addr_list + 1; *pcnt; pcnt++) {
+	    if (available >= required_per_addr) {
+		num_addrs++;
+		available -= required_per_addr;
+	    } else {
+		break;
+	    }
+	}
+    }
+    chk_available = available;
+    if (phost->h_aliases) {
+	for (pcnt = phost->h_aliases; *pcnt; pcnt++) {
+	    required_this_alias = sizeof(phost->h_aliases[0]) +
+		strlen(*pcnt) + 1;
+	    if (chk_available >= required_this_alias) {
+		num_aliases++;
+		chk_available -= required_this_alias;
+	    }
+	}
+    }
+
+    data->h.h_addrtype = phost->h_addrtype;
+    data->h.h_length = phost->h_length;
+    p_next_charptr = (char **) (void *) (rehostent + curlen);
+    p_next_char = rehostent + curlen;
+    if (phost->h_addr_list)
+	p_next_char += (size_t) (num_addrs + 1) * sizeof(phost->h_addr_list[0]);
+    if (phost->h_aliases)
+	p_next_char += (size_t) (num_aliases + 1) * sizeof(phost->h_aliases[0]);
+
+    if (phost->h_addr_list) {
+	data->h.h_addr_list = p_next_charptr;
+	for (pcnt = phost->h_addr_list, i_addr = 0;
+	     i_addr < num_addrs;
+	     pcnt++, i_addr++) {
+	    MemCpy(p_next_char, *pcnt, sizeof(phost->h_addr_list[0]));
+	    *p_next_charptr++ = p_next_char;
+	    p_next_char += sizeof(phost->h_addr_list[0]);
+	}
+	*p_next_charptr = NULL;
+    } else {
+	data->h.h_addr_list = NULL;
+    }
+
+    if (phost->h_name) {
+	data->h.h_name = p_next_char;
+	if (name_len) {
+	    strcpy(p_next_char, phost->h_name);
+	    p_next_char += name_len + 1;
+	} else {
+	    *p_next_char++ = '\0';
+	}
+    } else {
+	data->h.h_name = NULL;
+    }
+
+    if (phost->h_aliases) {
+	data->h.h_aliases = p_next_charptr;
+	for (pcnt = phost->h_aliases, i_alias = 0;
+	     (*pcnt && i_alias < num_addrs);
+	     pcnt++, i_alias++) {
+	    mem_this_alias = strlen(*pcnt) + 1;
+	    required_this_alias = sizeof(phost->h_aliases[0]) +
+		mem_this_alias;
+	    if (available >= required_this_alias) {
+		i_alias++;
+		available -= required_this_alias;
+		strcpy(p_next_char, *pcnt);
+		*p_next_charptr++ = p_next_char;
+		p_next_char += mem_this_alias;
+	    }
+	    p_next_char += sizeof(phost->h_aliases[0]);
+	}
+	*p_next_charptr = NULL;
+    } else {
+	data->h.h_aliases = NULL;
+    }
+    curlen = (size_t) (p_next_char - (char *) rehostent);
+    return curlen;
+}
+
+/*
+ * This chunk of code is used in both win32 and cygwin.
+ */
+#if defined(_WINDOWS_NSL)
+static LYNX_HOSTENT *gbl_phost;	/* Pointer to host - See netdb.h */
+
+#if !(defined(__CYGWIN__) && defined(NSL_FORK))
+static int donelookup;
+
+static unsigned long __stdcall _fork_func(void *arg)
+{
+    const char *host = (const char *) arg;
+    static AlignedHOSTENT aligned_full_rehostent;
+    char *rehostent = (char *) &aligned_full_rehostent;
+    size_t rehostentlen = 0;
+
+#ifdef SH_EX
+    unsigned long addr;
+
+    addr = (unsigned long) inet_addr(host);
+    if (addr != INADDR_NONE)
+	gbl_phost = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
+    else
+	gbl_phost = gethostbyname(host);
+#else
+    gbl_phost = gethostbyname(host);
+#endif
+
+    if (gbl_phost) {
+	rehostentlen = fill_rehostent(rehostent,
+				      (size_t) REHOSTENT_SIZE,
+				      gbl_phost);
+	if (rehostentlen == 0) {
+	    gbl_phost = (LYNX_HOSTENT *) NULL;
+	} else {
+	    gbl_phost = (LYNX_HOSTENT *) rehostent;
+	}
+    }
+
+    donelookup = TRUE;
+    return (unsigned long) (gbl_phost);
+}
+#endif /* __CYGWIN__ */
+#endif /* _WINDOWS_NSL */
+#endif /* NSL_FORK */
+
+#ifndef HAVE_H_ERRNO
+#undef  h_errno
+#define h_errno my_errno
+static int my_errno;
+
+#else /* we do HAVE_H_ERRNO: */
+#ifndef h_errno			/* there may be a macro as well as the extern data */
+extern int h_errno;
+#endif
+#endif
+
+/*
+ * Even though it is a small amount, we cannot count on reading the whole
+ * struct via a pipe in one read -TD
+ */
+#ifdef NSL_FORK
+static unsigned readit(int fd, char *buffer, size_t length)
+{
+    unsigned result = 0;
+
+    while (length != 0) {
+	unsigned got = (unsigned) read(fd, buffer, length);
+
+	if ((int) got > 0) {
+	    result += got;
+	    buffer += got;
+	    length -= got;
+	} else {
+	    break;
+	}
+    }
+    return result;
+}
+#endif /* NSL_FORK */
+
+/*	Resolve an internet hostname, like gethostbyname
+ *	------------------------------------------------
+ *
+ *  On entry,
+ *	str	points to the given host name, not numeric address,
+ *		without colon or port number.
+ *
+ *  On exit,
+ *	returns a pointer to a LYNX_HOSTENT in static storage,
+ *	or NULL in case of error or user interruption.
+ *
+ *  The interface is intended to be exactly the same as for (Unix)
+ *  gethostbyname(), except for the following:
+ *
+ *  If NSL_FORK is not used, the result of gethostbyname is returned
+ *  directly.  Otherwise:
+ *  All lists, addresses, and strings referred to by pointers in the
+ *  returned struct are located, together with the returned struct
+ *  itself, in a buffer of size REHOSTENT_SIZE.  If not everything fits,
+ *  some info is omitted, but the function is careful to still return
+ *  a valid structure, without truncating strings; it tries to return,
+ *  in order of decreasing priority, the first address (h_addr_list[0]), the
+ *  official name (h_name), the additional addresses, then alias names.
+ *
+ *  If NULL is returned, the reason is made available in the global
+ *  variable lynx_nsl_status, with one of the following values:
+ *	HT_INTERRUPTED		Interrupted by user
+ *	HT_NOT_ACCEPTABLE	Hostname detected as invalid
+ *				(also sets h_errno)
+ *	HT_H_ERRNO_VALID	An error occurred, and h_errno holds
+ *				an appropriate value
+ *	HT_ERROR		Resolver error, reason not known
+ *	HT_INTERNAL		Internal error
+ */
+LYNX_HOSTENT *LYGetHostByName(char *str)
+{
+    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
+     * rehostent[REHOSTENT_SIZE], but the indirect approach via the above
+     * struct should automatically take care of alignment requirements.
+     * Note that, in addition,
+     * - this must be static, as we shall return a pointer to it which must
+     *   remain valid, and
+     * - we have to use the same rehostent in the child process as in the
+     *   parent (its address in the parent's address space must be the same as
+     *   in the child's, otherwise the internal pointers built by the child's
+     *   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;
+
+#ifdef __DJGPP__
+    _resolve_hook = ResolveYield;
+#endif
+
+    if (!str) {
+	CTRACE((tfp, "LYGetHostByName: Can't parse `NULL'.\n"));
+	lynx_nsl_status = HT_INTERNAL;
+	return NULL;
+    }
+    CTRACE((tfp, "LYGetHostByName: parsing `%s'.\n", str));
+
+    /*  Could disable this if all our callers already check - kw */
+    if (HTCheckForInterrupt()) {
+	CTRACE((tfp, "LYGetHostByName: INTERRUPTED for '%s'.\n", str));
+	lynx_nsl_status = HT_INTERRUPTED;
+	return NULL;
+    }
+
+    if (!valid_hostname(host)) {
+	lynx_nsl_status = HT_NOT_ACCEPTABLE;
+#ifdef NO_RECOVERY
+#ifdef _WINDOWS
+	WSASetLastError(NO_RECOVERY);
+#else
+	h_errno = NO_RECOVERY;
+#endif
+#endif
+	return NULL;
+    }
+#ifdef MVS			/* Outstanding problem with crash in MVS gethostbyname */
+    CTRACE((tfp, "LYGetHostByName: Calling gethostbyname(%s)\n", host));
+#endif /* MVS */
+
+    CTRACE_FLUSH(tfp);		/* so child messages will not mess up parent log */
+
+    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;
+	}
+    }
+#else /* Not NSL_FORK: */
+
+#ifdef _WINDOWS_NSL
+    {
+	HANDLE hThread;
+	DWORD dwThreadID;
+
+#ifndef __CYGWIN__
+	if (!system_is_NT) {	/* for Windows9x */
+	    unsigned long t;
+
+	    t = (unsigned long) inet_addr(host);
+	    if (t != INADDR_NONE)
+		gbl_phost = gethostbyaddr((char *) &t, sizeof(t), AF_INET);
+	    else
+		gbl_phost = gethostbyname(host);
+	} else {		/* for Windows NT */
+#endif /* !__CYGWIN__ */
+	    gbl_phost = (LYNX_HOSTENT *) NULL;
+	    donelookup = FALSE;
+
+#if defined(__CYGWIN__) || defined(USE_WINSOCK2_H)
+	    SetLastError(WSAHOST_NOT_FOUND);
+#else
+	    WSASetLastError(WSAHOST_NOT_FOUND);
+#endif
+
+	    hThread = CreateThread(NULL, 4096UL, _fork_func, host, 0UL,
+				   &dwThreadID);
+	    if (!hThread)
+		MessageBox(NULL, "CreateThread",
+			   "CreateThread Failed", 0L);
+
+	    while (!donelookup) {
+		if (HTCheckForInterrupt()) {
+		    /* Note that host is a character array and is not freed */
+		    /* to avoid possible subthread problems: */
+		    if (!CloseHandle(hThread)) {
+			MessageBox((void *) NULL,
+				   "CloseHandle", "CloseHandle Failed", 0L);
+		    }
+		    lynx_nsl_status = HT_INTERRUPTED;
+		    return NULL;
+		}
+	    }
+#ifndef __CYGWIN__
+	}
+#endif /* !__CYGWIN__ */
+	if (gbl_phost) {
+	    lynx_nsl_status = HT_OK;
+	    result_phost = gbl_phost;
+	} else {
+	    lynx_nsl_status = HT_ERROR;
+	    goto failed;
+	}
+    }
+
+#else /* !NSL_FORK, !_WINDOWS_NSL: */
+    {
+	LYNX_HOSTENT *phost;
+
+	phost = gethostbyname(host);	/* See netdb.h */
+#ifdef MVS
+	CTRACE((tfp, "LYGetHostByName: gethostbyname() returned %d\n", phost));
+#endif /* MVS */
+	if (phost) {
+	    lynx_nsl_status = HT_OK;
+	    result_phost = phost;
+	} else {
+	    lynx_nsl_status = HT_H_ERRNO_VALID;
+	    goto failed;
+	}
+    }
+#endif /* !NSL_FORK, !_WINDOWS_NSL */
+#endif /* !NSL_FORK */
+
+#ifdef DEBUG_HOSTENT
+    dump_hostent("End of LYGetHostByName", result_phost);
+    CTRACE((tfp, "LYGetHostByName: Resolved name to a hostent.\n"));
+#endif
+
+    return result_phost;	/* OK */
+
+  failed:
+    CTRACE((tfp, "LYGetHostByName: Can't find internet node name `%s'.\n",
+	    host));
+    return NULL;
+}
+
+/*	Parse a network node address and port
+ *	-------------------------------------
+ *
+ *  On entry,
+ *	str	points to a string with a node name or number,
+ *		with optional trailing colon and port number.
+ *	soc_in	points to the binary internet or decnet address field.
+ *
+ *  On exit,
+ *	*soc_in is filled in.  If no port is specified in str, that
+ *		field is left unchanged in *soc_in.
+ */
+#ifndef INET6
+static int HTParseInet(SockA * soc_in, const char *str)
+{
+    char *port;
+    int dotcount_ip = 0;	/* for dotted decimal IP addr */
+    char *strptr;
+    char *host = NULL;
+
+    if (!str) {
+	CTRACE((tfp, "HTParseInet: Can't parse `NULL'.\n"));
+	return -1;
+    }
+    CTRACE((tfp, "HTParseInet: parsing `%s'.\n", str));
+    if (HTCheckForInterrupt()) {
+	CTRACE((tfp, "HTParseInet: INTERRUPTED for '%s'.\n", str));
+	return -1;
+    }
+    StrAllocCopy(host, str);	/* Make a copy we can mutilate */
+    /*
+     * Parse port number if present.
+     */
+    if ((port = strchr(host, ':')) != NULL) {
+	*port++ = 0;		/* Chop off port */
+	strptr = port;
+	if (port[0] >= '0' && port[0] <= '9') {
+#ifdef UNIX
+	    soc_in->sin_port = (PortNumber) htons(strtol(port, &strptr, 10));
+#else /* VMS: */
+#ifdef DECNET
+	    soc_in->sdn_objnum = (unsigned char) (strtol(port, &strptr, 10));
+#else
+	    soc_in->sin_port = htons((PortNumber) strtol(port, &strptr, 10));
+#endif /* Decnet */
+#endif /* Unix vs. VMS */
+#ifdef SUPPRESS			/* 1. crashes!?!.  2. URL syntax has number not name */
+	} else {
+	    struct servent *serv = getservbyname(port, (char *) 0);
+
+	    if (serv) {
+		soc_in->sin_port = serv->s_port;
+	    } else {
+		CTRACE((tfp, "TCP: Unknown service %s\n", port));
+	    }
+#endif /* SUPPRESS */
+	}
+	if (strptr && *strptr != '\0') {
+	    FREE(host);
+	    HTAlwaysAlert(NULL, gettext("Address has invalid port"));
+	    return -1;
+	}
+    }
+#ifdef DECNET
+    /*
+     * Read Decnet node name.  @@ Should know about DECnet addresses, but it's
+     * probably worth waiting until the Phase transition from IV to V.
+     */
+    soc_in->sdn_nam.n_len = min(DN_MAXNAML, strlen(host));	/* <=6 in phase 4 */
+    StrNCpy(soc_in->sdn_nam.n_name, host, soc_in->sdn_nam.n_len + 1);
+    CTRACE((tfp,
+	    "DECnet: Parsed address as object number %d on host %.6s...\n",
+	    soc_in->sdn_objnum, host));
+#else /* parse Internet host: */
+
+    if (*host >= '0' && *host <= '9') {		/* Test for numeric node address: */
+	strptr = host;
+	while (*strptr) {
+	    if (*strptr == '.') {
+		dotcount_ip++;
+	    } else if (!isdigit(UCH(*strptr))) {
+		break;
+	    }
+	    strptr++;
+	}
+	if (*strptr) {		/* found non-numeric, assume domain name */
+	    dotcount_ip = 0;
+	}
+    }
+
+    /*
+     * Parse host number if present.
+     */
+    if (dotcount_ip == 3)	/* Numeric node address: */
+    {
+#ifdef DGUX_OLD
+	soc_in->sin_addr.s_addr = inet_addr(host).s_addr;	/* See arpa/inet.h */
+#else
+#ifdef GUSI
+	soc_in->sin_addr = inet_addr(host);	/* See netinet/in.h */
+#else
+#ifdef HAVE_INET_ATON
+	if (!inet_aton(host, &(soc_in->sin_addr))) {
+	    CTRACE((tfp, "inet_aton(%s) returns error\n", host));
+	    FREE(host);
+	    return -1;
+	}
+#else
+	soc_in->sin_addr.s_addr = inet_addr(host);	/* See arpa/inet.h */
+#endif /* HAVE_INET_ATON */
+#endif /* GUSI */
+#endif /* DGUX_OLD */
+	FREE(host);
+    } else {			/* Alphanumeric node name: */
+
+#ifdef MVS			/* Outstanding problem with crash in MVS gethostbyname */
+	CTRACE((tfp, "HTParseInet: Calling LYGetHostByName(%s)\n", host));
+#endif /* MVS */
+
+#ifdef _WINDOWS_NSL
+	gbl_phost = LYGetHostByName(host);	/* See above */
+	if (!gbl_phost)
+	    goto failed;
+	MemCpy((void *) &soc_in->sin_addr, gbl_phost->h_addr_list[0], gbl_phost->h_length);
+#else /* !_WINDOWS_NSL */
+	{
+	    LYNX_HOSTENT *phost;
+
+	    phost = LYGetHostByName(host);	/* See above */
+
+	    if (!phost)
+		goto failed;
+	    if (!phost)
+		goto failed;
+	    if (phost->h_length != sizeof soc_in->sin_addr) {
+		HTAlwaysAlert(host, gettext("Address length looks invalid"));
+	    }
+	    MemCpy((void *) &soc_in->sin_addr, phost->h_addr_list[0], phost->h_length);
+	}
+#endif /* _WINDOWS_NSL */
+
+	FREE(host);
+    }				/* Alphanumeric node name */
+
+    CTRACE((tfp,
+	    "HTParseInet: Parsed address as port %d, IP address %d.%d.%d.%d\n",
+	    (int) ntohs(soc_in->sin_port),
+	    (int) *((unsigned char *) (&soc_in->sin_addr) + 0),
+	    (int) *((unsigned char *) (&soc_in->sin_addr) + 1),
+	    (int) *((unsigned char *) (&soc_in->sin_addr) + 2),
+	    (int) *((unsigned char *) (&soc_in->sin_addr) + 3)));
+#endif /* Internet vs. Decnet */
+
+    return 0;			/* OK */
+
+  failed:
+    CTRACE((tfp, "HTParseInet: Can't find internet node name `%s'.\n",
+	    host));
+    FREE(host);
+    switch (lynx_nsl_status) {
+    case HT_NOT_ACCEPTABLE:
+    case HT_INTERRUPTED:
+	return lynx_nsl_status;
+    default:
+	return -1;
+    }
+}
+#endif /* !INET6 */
+
+#ifdef INET6
+static LYNX_ADDRINFO *HTGetAddrInfo(const char *str,
+				    const int defport)
+{
+    LYNX_ADDRINFO hints, *res;
+    int error;
+    char *p;
+    char *s = NULL;
+    char *host, *port;
+    char pbuf[80];
+
+    StrAllocCopy(s, str);
+
+    if (s[0] == '[' && (p = strchr(s, ']')) != NULL) {
+	*p++ = '\0';
+	host = s + 1;
+    } else {
+	p = s;
+	host = &s[0];
+    }
+    port = strrchr(p, ':');
+    if (port) {
+	*port++ = '\0';
+    } else {
+	sprintf(pbuf, "%d", defport);
+	port = pbuf;
+    }
+
+    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)));
+	res = NULL;
+    }
+
+    free(s);
+    return res;
+}
+#endif /* INET6 */
+
+#ifdef LY_FIND_LEAKS
+/*	Free our name for the host on which we are - FM
+ *	-------------------------------------------
+ *
+ */
+static void free_HTTCP_hostname(void)
+{
+    FREE(hostname);
+}
+#endif /* LY_FIND_LEAKS */
+
+/*	Derive the name of the host on which we are
+ *	-------------------------------------------
+ *
+ */
+static void get_host_details(void)
+{
+    char name[MAXHOSTNAMELEN + 1];	/* The name of this host */
+
+#ifdef UCX
+    char *domain_name;		/* The name of this host domain */
+#endif /* UCX */
+#ifdef NEED_HOST_ADDRESS	/* no -- needs name server! */
+#ifdef INET6
+    LYNX_ADDRINFO hints, *res;
+    int error;
+
+#else
+    LYNX_HOSTENT *phost;	/* Pointer to host -- See netdb.h */
+#endif /* INET6 */
+#endif /* NEED_HOST_ADDRESS */
+    size_t namelength = sizeof(name);
+
+    if (hostname)
+	return;			/* Already done */
+    gethostname(name, namelength);	/* Without domain */
+    StrAllocCopy(hostname, name);
+#ifdef LY_FIND_LEAKS
+    atexit(free_HTTCP_hostname);
+#endif
+#ifdef UCX
+    /*
+     * UCX doesn't give the complete domain name.  Get rest from UCX$BIND_DOM
+     * logical.
+     */
+    if (strchr(hostname, '.') == NULL) {	/* Not full address */
+	domain_name = LYGetEnv("UCX$BIND_DOMAIN");
+	if (domain_name == NULL)
+	    domain_name = LYGetEnv("TCPIP$BIND_DOMAIN");
+	if (domain_name != NULL) {
+	    StrAllocCat(hostname, ".");
+	    StrAllocCat(hostname, domain_name);
+	}
+    }
+#endif /* UCX */
+    CTRACE((tfp, "TCP: Local host name is %s\n", hostname));
+
+#ifndef DECNET			/* Decnet ain't got no damn name server 8#OO */
+#ifdef NEED_HOST_ADDRESS	/* no -- needs name server! */
+#ifdef INET6
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_CANONNAME;
+    error = getaddrinfo(name, NULL, &hints, &res);
+    if (error || !res || !res->ai_canonname) {
+	CTRACE((tfp, "TCP: %s: `%s'\n", gai_strerror(error), name));
+	if (res)
+	    freeaddrinfo(res);
+	return;			/* Fail! */
+    }
+    StrAllocCopy(hostname, res->ai_canonname);
+    MemCpy(&HTHostAddress, res->ai_addr, res->ai_addrlen);
+    freeaddrinfo(res);
+#else
+    phost = gethostbyname(name);	/* See netdb.h */
+    if (!OK_HOST(phost)) {
+	CTRACE((tfp,
+		"TCP: Can't find my own internet node address for `%s'!!\n",
+		name));
+	return;			/* Fail! */
+    }
+    StrAllocCopy(hostname, phost->h_name);
+    MemCpy(&HTHostAddress, &phost->h_addr_list[0], phost->h_length);
+#endif /* INET6 */
+    CTRACE((tfp, "     Name server says that I am `%s' = %s\n",
+	    hostname, HTInetString(&HTHostAddress)));
+#endif /* NEED_HOST_ADDRESS */
+
+#endif /* !DECNET */
+}
+
+const char *HTHostName(void)
+{
+    get_host_details();
+    return hostname;
+}
+
+#ifdef _WINDOWS
+#define SET_EINTR WSASetLastError(EINTR)
+#else
+#define SET_EINTR SOCKET_ERRNO = EINTR
+#endif
+
+static BOOL HTWasInterrupted(int *status)
+{
+    BOOL result = FALSE;
+
+    if (HTCheckForInterrupt()) {
+	result = TRUE;
+	*status = HT_INTERRUPTED;
+	SET_EINTR;
+    }
+    return result;
+}
+
+#define TRIES_PER_SECOND 10
+
+/*
+ * Set the select-timeout to 0.1 seconds.
+ */
+static void set_timeout(struct timeval *timeoutp)
+{
+    timeoutp->tv_sec = 0;
+    timeoutp->tv_usec = 100000;
+}
+
+#ifndef MULTINET		/* SOCKET_ERRNO != errno ? */
+#if !defined(UCX) || !defined(VAXC)	/* errno not modifiable ? */
+#define SOCKET_DEBUG_TRACE	/* show errno status after some system calls */
+#endif /* UCX && VAXC */
+#endif /* MULTINET */
+/*
+ *  Interruptible connect as implemented for Mosaic by Marc Andreesen
+ *  and hacked in for Lynx years ago by Lou Montulli, and further
+ *  modified over the years by numerous Lynx lovers. - FM
+ */
+int HTDoConnect(const char *url,
+		const char *protocol,
+		int default_port,
+		int *s)
+{
+    int status = 0;
+    char *line = NULL;
+    char *p1 = NULL;
+    char *at_sign = NULL;
+    char *host = NULL;
+
+#ifdef INET6
+    LYNX_ADDRINFO *res = 0, *res0 = 0;
+
+#else
+    struct sockaddr_in soc_address;
+    struct sockaddr_in *soc_in = &soc_address;
+
+    /*
+     * Set up defaults.
+     */
+    memset(soc_in, 0, sizeof(*soc_in));
+    soc_in->sin_family = AF_INET;
+    soc_in->sin_port = htons((PortNumber) default_port);
+#endif /* INET6 */
+
+    /*
+     * Get node name and optional port number.
+     */
+    p1 = HTParse(url, "", PARSE_HOST);
+    if ((at_sign = strchr(p1, '@')) != NULL) {
+	/*
+	 * If there's an @ then use the stuff after it as a hostname.
+	 */
+	StrAllocCopy(host, (at_sign + 1));
+    } else {
+	StrAllocCopy(host, p1);
+    }
+    FREE(p1);
+
+    HTSprintf0(&line, "%s%s", WWW_FIND_MESSAGE, host);
+    _HTProgress(line);
+#ifdef INET6
+    /* HTParseInet() is useless! */
+    res0 = HTGetAddrInfo(host, default_port);
+    if (res0 == NULL) {
+	HTSprintf0(&line, gettext("Unable to locate remote host %s."), host);
+	_HTProgress(line);
+	FREE(host);
+	FREE(line);
+	return HT_NO_DATA;
+    }
+#else
+    status = HTParseInet(soc_in, host);
+    if (status) {
+	if (status != HT_INTERRUPTED) {
+	    if (status == HT_NOT_ACCEPTABLE) {
+		/* Not HTProgress, so warning won't be overwritten immediately;
+		 * but not HTAlert, because typically there will be other
+		 * alerts from the callers.  - kw
+		 */
+		HTUserMsg2(gettext("Invalid hostname %s"), host);
+	    } else {
+		HTSprintf0(&line,
+			   gettext("Unable to locate remote host %s."), host);
+		_HTProgress(line);
+	    }
+	    status = HT_NO_DATA;
+	}
+	FREE(host);
+	FREE(line);
+	return status;
+    }
+#endif /* INET6 */
+
+    HTSprintf0(&line, gettext("Making %s connection to %s"), protocol, host);
+    _HTProgress(line);
+    FREE(host);
+    FREE(line);
+
+    /*
+     * Now, let's get a socket set up from the server for the data.
+     */
+#ifndef INET6
+    *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (*s == -1) {
+	HTAlert(gettext("socket failed."));
+	return HT_NO_DATA;
+    }
+#else
+    for (res = res0; res; res = res->ai_next) {
+	*s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+	if (*s == -1) {
+	    char hostbuf[1024], portbuf[1024];
+
+	    getnameinfo(res->ai_addr, res->ai_addrlen,
+			hostbuf, (socklen_t) sizeof(hostbuf),
+			portbuf, (socklen_t) sizeof(portbuf),
+			NI_NUMERICHOST | NI_NUMERICSERV);
+	    HTSprintf0(&line,
+		       gettext("socket failed: family %d addr %s port %s."),
+		       res->ai_family, hostbuf, portbuf);
+	    _HTProgress(line);
+	    FREE(line);
+	    continue;
+	}
+#endif /* INET6 */
+
+#if !defined(DOSPATH) || defined(__DJGPP__)
+#if !defined(NO_IOCTL) || defined(USE_FCNTL)
+	/*
+	 * Make the socket non-blocking, so the connect can be canceled.  This
+	 * means that when we issue the connect we should NOT have to wait for
+	 * the accept on the other end.
+	 */
+	{
+#ifdef USE_FCNTL
+	    int ret = fcntl(*s, F_SETFL, O_NONBLOCK);
+
+#else
+	    int val = 1;
+	    int ret = IOCTL(*s, FIONBIO, &val);
+#endif /* USE_FCNTL */
+	    if (ret == -1)
+		_HTProgress(gettext("Could not make connection non-blocking."));
+	}
+#endif /* !NO_IOCTL || USE_FCNTL */
+#endif /* !DOSPATH || __DJGPP__ */
+
+	/*
+	 * Issue the connect.  Since the server can't do an instantaneous
+	 * accept and we are non-blocking, this will almost certainly return a
+	 * negative status.
+	 */
+#ifdef SOCKS
+	if (socks_flag) {
+#ifdef INET6
+	    status = Rconnect(*s, res->ai_addr, res->ai_addrlen);
+#else
+	    status = Rconnect(*s, (struct sockaddr *) &soc_address,
+			      sizeof(soc_address));
+#ifndef SHORTENED_RBIND
+	    socks_bind_remoteAddr = soc_address.sin_addr.s_addr;
+#endif
+#endif /* INET6 */
+	} else
+#endif /* SOCKS */
+#ifdef INET6
+	    status = connect(*s, res->ai_addr, res->ai_addrlen);
+#else
+	    status = connect(*s, (struct sockaddr *) &soc_address, sizeof(soc_address));
+#endif /* INET6 */
+
+	/*
+	 * According to the Sun man page for connect:
+	 *  EINPROGRESS         The socket is non-blocking and the  con-
+	 *                      nection cannot be completed immediately.
+	 *                      It is possible to select(2) for  comple-
+	 *                      tion  by  selecting the socket for writ-
+	 *                      ing.
+	 * According to the Motorola SVR4 man page for connect:
+	 *  EAGAIN              The socket is non-blocking and the  con-
+	 *                      nection cannot be completed immediately.
+	 *                      It is possible to select for  completion
+	 *                      by  selecting  the  socket  for writing.
+	 *                      However, this is only  possible  if  the
+	 *                      socket  STREAMS  module  is  the topmost
+	 *                      module on  the  protocol  stack  with  a
+	 *                      write  service  procedure.  This will be
+	 *                      the normal case.
+	 */
+	if ((status < 0) &&
+	    (SOCKET_ERRNO == EINPROGRESS
+#ifdef EAGAIN
+	     || SOCKET_ERRNO == EAGAIN
+#endif
+	    )) {
+	    struct timeval select_timeout;
+	    int ret;
+	    int tries = 0;
+
+#ifdef SOCKET_DEBUG_TRACE
+	    HTInetStatus("this socket's first connect");
+#endif /* SOCKET_DEBUG_TRACE */
+	    ret = 0;
+	    while (ret <= 0) {
+		fd_set writefds;
+
+		/*
+		 * Protect against an infinite loop.
+		 */
+		if ((tries++ / TRIES_PER_SECOND) >= connect_timeout) {
+		    HTAlert(gettext("Connection failed (too many retries)."));
+#ifdef INET6
+		    FREE(line);
+		    if (res0)
+			freeaddrinfo(res0);
+#endif /* INET6 */
+		    return HT_NO_DATA;
+		}
+		set_timeout(&select_timeout);
+		FD_ZERO(&writefds);
+		FD_SET((unsigned) *s, &writefds);
+#ifdef SOCKS
+		if (socks_flag)
+		    ret = Rselect(*s + 1, NULL,
+				  &writefds, NULL, &select_timeout);
+		else
+#endif /* SOCKS */
+		    ret = select(*s + 1,
+				 NULL,
+				 &writefds,
+				 NULL,
+				 &select_timeout);
+
+#ifdef SOCKET_DEBUG_TRACE
+		if (tries == 1) {
+		    HTInetStatus("this socket's first select");
+		}
+#endif /* SOCKET_DEBUG_TRACE */
+		/*
+		 * If we suspend, then it is possible that select will be
+		 * interrupted.  Allow for this possibility.  - JED
+		 */
+		if ((ret == -1) && (errno == EINTR))
+		    continue;
+
+#ifdef SOCKET_DEBUG_TRACE
+		if (ret < 0) {
+		    HTInetStatus("failed select");
+		}
+#endif /* SOCKET_DEBUG_TRACE */
+		/*
+		 * Again according to the Sun and Motorola man pages for
+		 * connect:
+		 *  EALREADY    The socket is non-blocking and a  previ-
+		 *              ous  connection attempt has not yet been
+		 *              completed.
+		 * Thus if the SOCKET_ERRNO is NOT EALREADY we have a real
+		 * error, and should break out here and return that error.
+		 * Otherwise if it is EALREADY keep on trying to complete the
+		 * connection.
+		 */
+		if ((ret < 0) && (SOCKET_ERRNO != EALREADY)) {
+		    status = ret;
+		    break;
+		} else if (ret > 0) {
+		    /*
+		     * Extra check here for connection success, if we try to
+		     * connect again, and get EISCONN, it means we have a
+		     * successful connection.  But don't check with SOCKS.
+		     */
+#ifdef SOCKS
+		    if (socks_flag) {
+			status = 0;
+		    } else {
+#endif /* SOCKS */
+#ifdef INET6
+			status = connect(*s, res->ai_addr, res->ai_addrlen);
+#else
+			status = connect(*s, (struct sockaddr *) &soc_address,
+					 sizeof(soc_address));
+#endif /* INET6 */
+#ifdef UCX
+			/*
+			 * A UCX feature:  Instead of returning EISCONN UCX
+			 * returns EADDRINUSE.  Test for this status also.
+			 */
+			if ((status < 0) && ((SOCKET_ERRNO == EISCONN) ||
+					     (SOCKET_ERRNO == EADDRINUSE)))
+#else
+			if ((status < 0) && (SOCKET_ERRNO == EISCONN))
+#endif /* UCX */
+			{
+			    status = 0;
+			}
+
+			if (status && (SOCKET_ERRNO == EALREADY))	/* new stuff LJM */
+			    ret = 0;	/* keep going */
+			else {
+#ifdef SOCKET_DEBUG_TRACE
+			    if (status < 0) {
+				HTInetStatus("confirm-ready connect");
+			    }
+#endif /* SOCKET_DEBUG_TRACE */
+			    break;
+			}
+#ifdef SOCKS
+		    }
+#endif /* SOCKS */
+		}
+#ifdef SOCKS
+		else if (!socks_flag)
+#else
+		else
+#endif /* SOCKS */
+		{
+		    /*
+		     * The select says we aren't ready yet.  Try to connect
+		     * again to make sure.  If we don't get EALREADY or
+		     * EISCONN, something has gone wrong.  Break out and report
+		     * it.
+		     *
+		     * For some reason, SVR4 returns EAGAIN here instead of
+		     * EALREADY, even though the man page says it should be
+		     * EALREADY.
+		     *
+		     * For some reason, UCX pre 3 apparently returns errno =
+		     * 18242 instead of EALREADY or EISCONN.
+		     */
+#ifdef INET6
+		    status = connect(*s, res->ai_addr, res->ai_addrlen);
+#else
+		    status = connect(*s, (struct sockaddr *) &soc_address,
+				     sizeof(soc_address));
+#endif /* INET6 */
+		    if ((status < 0) &&
+			(SOCKET_ERRNO != EALREADY
+#ifdef EAGAIN
+			 && SOCKET_ERRNO != EAGAIN
+#endif
+			) &&
+#ifdef UCX
+			(SOCKET_ERRNO != 18242) &&
+#endif /* UCX */
+			(SOCKET_ERRNO != EISCONN)) {
+#ifdef SOCKET_DEBUG_TRACE
+			HTInetStatus("confirm-not-ready connect");
+#endif /* SOCKET_DEBUG_TRACE */
+			break;
+		    }
+		}
+		if (HTWasInterrupted(&status)) {
+		    CTRACE((tfp, "*** INTERRUPTED in middle of connect.\n"));
+		    break;
+		}
+	    }
+	}
+#ifdef SOCKET_DEBUG_TRACE
+	else if (status < 0) {
+	    HTInetStatus("this socket's first and only connect");
+	}
+#endif /* SOCKET_DEBUG_TRACE */
+#ifdef INET6
+	if (status < 0) {
+	    NETCLOSE(*s);
+	    *s = -1;
+	    continue;
+	}
+	break;
+    }
+#endif /* INET6 */
+
+#ifdef INET6
+    if (*s < 0)
+#else
+    if (status < 0)
+#endif /* INET6 */
+    {
+	/*
+	 * The connect attempt failed or was interrupted, so close up the
+	 * socket.
+	 */
+	NETCLOSE(*s);
+    }
+#if !defined(DOSPATH) || defined(__DJGPP__)
+#if !defined(NO_IOCTL) || defined(USE_FCNTL)
+    else {
+	/*
+	 * Make the socket blocking again on good connect.
+	 */
+#ifdef USE_FCNTL
+	int ret = fcntl(*s, F_SETFL, 0);
+
+#else
+	int val = 0;
+	int ret = IOCTL(*s, FIONBIO, &val);
+#endif /* USE_FCNTL */
+	if (ret == -1)
+	    _HTProgress(gettext("Could not restore socket to blocking."));
+    }
+#endif /* !NO_IOCTL || USE_FCNTL */
+#endif /* !DOSPATH || __DJGPP__ */
+
+#ifdef INET6
+    FREE(line);
+    if (res0)
+	freeaddrinfo(res0);
+#endif /* INET6 */
+    return status;
+}
+
+/*
+ *  This is so interruptible reads can be implemented cleanly.
+ */
+int HTDoRead(int fildes,
+	     void *buf,
+	     unsigned nbyte)
+{
+    int result;
+    BOOL ready;
+
+#if !defined(NO_IOCTL)
+    int ret;
+    fd_set readfds;
+    struct timeval select_timeout;
+    int tries = 0;
+
+#ifdef USE_READPROGRESS
+    int otries = 0;
+    time_t otime = time((time_t *) 0);
+    time_t start = otime;
+#endif
+#endif /* !NO_IOCTL */
+
+#if defined(UNIX) && !defined(__BEOS__)
+    if (fildes == 0) {
+	/*
+	 * 0 can be a valid socket fd, but if it's a tty something must have
+	 * gone wrong.  - kw
+	 */
+	if (isatty(fildes)) {
+	    CTRACE((tfp, "HTDoRead - refusing to read fd 0 which is a tty!\n"));
+	    return -1;
+	}
+    } else
+#endif
+    if (fildes <= 0) {
+	CTRACE((tfp, "HTDoRead - no file descriptor!\n"));
+	return -1;
+    }
+
+    if (HTWasInterrupted(&result)) {
+	CTRACE((tfp, "HTDoRead - interrupted before starting!\n"));
+	return (result);
+    }
+#if defined(NO_IOCTL)
+    ready = TRUE;
+#else
+    ready = FALSE;
+    while (!ready) {
+	/*
+	 * Protect against an infinite loop.
+	 */
+	if ((tries++ / TRIES_PER_SECOND) >= reading_timeout) {
+	    HTAlert(gettext("Socket read failed (too many tries)."));
+	    SET_EINTR;
+	    result = HT_INTERRUPTED;
+	    break;
+	}
+#ifdef USE_READPROGRESS
+	if (tries - otries > TRIES_PER_SECOND) {
+	    time_t t = time((time_t *) 0);
+
+	    otries = tries;
+	    if (t - otime >= 5) {
+		otime = t;
+		HTReadProgress((off_t) (-1), (off_t) 0);	/* Put "stalled" message */
+	    }
+	}
+#endif
+
+	/*
+	 * If we suspend, then it is possible that select will be interrupted.
+	 * Allow for this possibility.  - JED
+	 */
+	do {
+	    set_timeout(&select_timeout);
+	    FD_ZERO(&readfds);
+	    FD_SET((unsigned) fildes, &readfds);
+#ifdef SOCKS
+	    if (socks_flag)
+		ret = Rselect(fildes + 1,
+			      &readfds, NULL, NULL, &select_timeout);
+	    else
+#endif /* SOCKS */
+		ret = select(fildes + 1,
+			     &readfds, NULL, NULL, &select_timeout);
+	} while ((ret == -1) && (errno == EINTR));
+
+	if (ret < 0) {
+	    result = -1;
+	    break;
+	} else if (ret > 0) {
+	    ready = TRUE;
+	} else if (HTWasInterrupted(&result)) {
+	    break;
+	}
+    }
+#endif /* !NO_IOCTL */
+
+    if (ready) {
+#if defined(UCX) && defined(VAXC)
+	/*
+	 * VAXC and UCX problem only.
+	 */
+	errno = vaxc$errno = 0;
+	result = SOCKET_READ(fildes, buf, nbyte);
+	CTRACE((tfp,
+		"Read - result,errno,vaxc$errno: %d %d %d\n", result, errno, vaxc$errno));
+	if ((result <= 0) && TRACE)
+	    perror("HTTCP.C:HTDoRead:read");	/* RJF */
+	/*
+	 * An errno value of EPIPE and result < 0 indicates end-of-file on VAXC.
+	 */
+	if ((result <= 0) && (errno == EPIPE)) {
+	    result = 0;
+	    set_errno(0);
+	}
+#else
+#ifdef UNIX
+	while ((result = (int) SOCKET_READ(fildes, buf, nbyte)) == -1) {
+	    if (errno == EINTR)
+		continue;
+#ifdef ERESTARTSYS
+	    if (errno == ERESTARTSYS)
+		continue;
+#endif /* ERESTARTSYS */
+	    HTInetStatus("read");
+	    break;
+	}
+#else /* UNIX */
+	result = SOCKET_READ(fildes, buf, nbyte);
+#endif /* !UNIX */
+#endif /* UCX && VAXC */
+    }
+#ifdef USE_READPROGRESS
+    CTRACE2(TRACE_TIMING, (tfp, "...HTDoRead returns %d (%" PRI_time_t
+			   " seconds)\n",
+			   result, CAST_time_t (time((time_t *)0) - start)));
+#endif
+    return result;
+}
+
+#ifdef SVR4_BSDSELECT
+/*
+ *  This is a fix for the difference between BSD's select() and
+ *  SVR4's select().  SVR4's select() can never return a value larger
+ *  than the total number of file descriptors being checked.  So, if
+ *  you select for read and write on one file descriptor, and both
+ *  are true, SVR4 select() will only return 1.  BSD select in the
+ *  same situation will return 2.
+ *
+ *	Additionally, BSD select() on timing out, will zero the masks,
+ *	while SVR4 does not.  This is fixed here as well.
+ *
+ *	Set your tabstops to 4 characters to have this code nicely formatted.
+ *
+ *	Jerry Whelan, guru@bradley.edu, June 12th, 1993
+ */
+#ifdef select
+#undef select
+#endif /* select */
+
+#ifdef SOCKS
+#ifdef Rselect
+#undef Rselect
+#endif /* Rselect */
+#endif /* SOCKS */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+int BSDselect(int nfds,
+	      fd_set * readfds,
+	      fd_set * writefds,
+	      fd_set * exceptfds,
+	      struct timeval *select_timeout)
+{
+    int rval, i;
+
+#ifdef SOCKS
+    if (socks_flag)
+	rval = Rselect(nfds, readfds, writefds, exceptfds, select_timeout);
+    else
+#endif /* SOCKS */
+	rval = select(nfds, readfds, writefds, exceptfds, select_timeout);
+
+    switch (rval) {
+    case -1:
+	return (rval);
+
+    case 0:
+	if (readfds != NULL)
+	    FD_ZERO(readfds);
+	if (writefds != NULL)
+	    FD_ZERO(writefds);
+	if (exceptfds != NULL)
+	    FD_ZERO(exceptfds);
+	return (rval);
+
+    default:
+	for (i = 0, rval = 0; i < nfds; i++) {
+	    if ((readfds != NULL) && FD_ISSET(i, readfds))
+		rval++;
+	    if ((writefds != NULL) && FD_ISSET(i, writefds))
+		rval++;
+	    if ((exceptfds != NULL) && FD_ISSET(i, exceptfds))
+		rval++;
+
+	}
+	return (rval);
+    }
+/* Should never get here */
+}
+#endif /* SVR4_BSDSELECT */