about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation/HTTCP.c
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>1999-02-08 10:50:02 -0500
committerThomas E. Dickey <dickey@invisible-island.net>1999-02-08 10:50:02 -0500
commit8ce6b560f4fb325be3d34266c54c70eb8668e8e1 (patch)
treed227c501d100ee0c5f1c72601d9ea5a487c1e2ca /WWW/Library/Implementation/HTTCP.c
parent87434eaa074d789f65bac589b03df341e76e7a4e (diff)
downloadlynx-snapshots-8ce6b560f4fb325be3d34266c54c70eb8668e8e1.tar.gz
snapshot of project "lynx", label v2-8-2dev_16
Diffstat (limited to 'WWW/Library/Implementation/HTTCP.c')
-rw-r--r--WWW/Library/Implementation/HTTCP.c961
1 files changed, 685 insertions, 276 deletions
diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c
index d58b86d4..f34e0fbf 100644
--- a/WWW/Library/Implementation/HTTCP.c
+++ b/WWW/Library/Implementation/HTTCP.c
@@ -28,7 +28,11 @@
 #include <sys/wait.h>
 #endif /* NSL_FORK */
 
-#define OK_HOST(p) ((p) != 0 && (p->h_length) != 0)
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+#define OK_HOST(p) ((p) != 0 && ((p)->h_length) != 0)
 
 #ifdef SVR4_BSDSELECT
 PUBLIC int BSDselect PARAMS((
@@ -368,152 +372,337 @@ PRIVATE void quench ARGS1(
 }
 #endif /* NSL_FORK */
 
-/*	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.
+PUBLIC int lynx_nsl_status = HT_OK;
+
+#ifndef DJGPP			/* much excluded! */
+
+#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 struct hostent.
 **
-**  On exit,
-**	*soc_in is filled in.  If no port is specified in str, that
-**		field is left unchanged in *soc_in.
+**  dump_hostent - dumps the contents of a struct hostent to the
+**  trace log or stderr, including all pointer values, strings, and
+**  addresses, in a format inspired by gdb's print format. - kw
 */
-PUBLIC int HTParseInet ARGS2(
-	SockA *,	soc_in,
-	CONST char *,	str)
+PRIVATE void dump_hostent ARGS2(
+    CONST char *,		msgprefix,
+    CONST struct hostent *,	phost)
 {
-    char *port;
-    int dotcount_ip = 0;	/* for dotted decimal IP addr */
-#ifndef _WINDOWS_NSL
-    char *host = NULL;
-#endif /* _WINDOWS_NSL */
-
-    if (!str) {
-	CTRACE(tfp, "HTParseInet: Can't parse `NULL'.\n");
-	return -1;
+    if (TRACE) {
+	int i;
+	char **pcnt;
+	CTRACE(tfp,"%s: %p ", msgprefix, 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", 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", 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);
     }
-    CTRACE(tfp, "HTParseInet: parsing `%s'.\n", str);
-    if (HTCheckForInterrupt()) {
-	CTRACE (tfp, "HTParseInet: INTERRUPTED for '%s'.\n", str);
-	return -1;
+}
+
+/*
+**  fill_rehostent - copies as much as possible relevant content from
+**  the struct 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 struct hostent. - kw
+**  See also description of LYGetHostByName.
+*/
+PRIVATE size_t fill_rehostent ARGS3(
+    char *,			rehostent,
+    size_t,			rehostentsize,
+    CONST struct hostent *,	phost)
+{
+    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(struct 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 = 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;
+	    }
+	}
     }
-#ifdef _WINDOWS_NSL
-    strncpy(host, str, (size_t)512);
-#else
-    StrAllocCopy(host, str);	/* Make a copy we can mutilate */
-#endif /*  _WINDOWS_NSL */
-    /*
-    **	Parse port number if present.
-    */
-    if ((port = strchr(host, ':')) != NULL) {
-	*port++ = 0;		/* Chop off port */
-	if (port[0] >= '0' && port[0] <= '9') {
-#ifdef unix
-	    soc_in->sin_port = htons(atol(port));
-#else /* VMS: */
-#ifdef DECNET
-	    soc_in->sdn_objnum = (unsigned char)(strtol(port, (char**)0, 10));
-#else
-	    soc_in->sin_port = htons((unsigned short)strtol(port,(char**)0,10));
-#endif /* Decnet */
-#endif /* Unix vs. VMS */
-#ifdef SUPPRESS 	/* 1. crashes!?!.  2. Not recommended */
+    if (phost->h_name) {
+	name_len = strlen(phost->h_name);
+	if (available >= name_len) {
+	    available -= name_len;
 	} else {
-	    struct servent * serv = getservbyname(port, (char*)0);
-	    if (serv) {
-		soc_in->sin_port = serv->s_port;
+	    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 {
-		CTRACE(tfp, "TCP: Unknown service %s\n", port);
+		break;
 	    }
-#endif /* SUPPRESS */
 	}
     }
-
-#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: */
-	char *strptr = host;
-	while (*strptr) {
-	    if (*strptr == '.') {
-		dotcount_ip++;
-	    } else if (!isdigit(*strptr)) {
-		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;
 	    }
-	    strptr++;
 	}
-	if (*strptr) {		/* found non-numeric, assume domain name */
-	    dotcount_ip = 0;
+    }
+
+    ((struct hostent *)rehostent)->h_addrtype = phost->h_addrtype;
+    ((struct hostent *)rehostent)->h_length = phost->h_length;
+    p_next_charptr = (char **)(rehostent + curlen);
+    p_next_char = rehostent + curlen;
+    if (phost->h_addr_list)
+	p_next_char += (num_addrs+1) * sizeof(phost->h_addr_list[0]);
+    if (phost->h_aliases)
+	p_next_char += (num_aliases+1) * sizeof(phost->h_aliases[0]);
+
+    if (phost->h_addr_list) {
+	((struct hostent *)rehostent)->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 {
+	((struct hostent *)rehostent)->h_addr_list = NULL;
     }
 
-    /*
-    **	Parse host number if present.
-    */
-    if (dotcount_ip == 3) {   /* Numeric node address: */
+    if (phost->h_name) {
+	((struct hostent *)rehostent)->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 {
+	((struct hostent *)rehostent)->h_name = NULL;
+    }
 
-#ifdef DJGPP
-	soc_in->sin_addr.s_addr = htonl(aton(host));
-#else
-#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);
-	    return -1;
+    if (phost->h_aliases) {
+	((struct hostent *)rehostent)->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]);
 	}
-#else
-	soc_in->sin_addr.s_addr = inet_addr(host);	/* See arpa/inet.h */
-#endif /* HAVE_INET_ATON */
-#endif /* GUSI */
-#endif /* DGUX_OLD */
-#endif /* DJGPP */
-#ifndef _WINDOWS_NSL
-	FREE(host);
-#endif /* _WINDOWS_NSL */
-    } else {		    /* Alphanumeric node name: */
-	if (!valid_hostname(host)) {
+	*p_next_charptr++ = NULL;
+    } else {
+	((struct hostent *)rehostent)->h_aliases = NULL;
+    }
+    curlen = p_next_char - (char *)rehostent;
+    return curlen;
+}
+
+#define REHOSTENT_SIZE 128		/* not bigger than pipe buffer! */
+
+/*	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 struct 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), 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
+*/
+PUBLIC struct hostent * LYGetHostByName ARGS1(
+	CONST char *,	str)
+{
 #ifndef _WINDOWS_NSL
-	    FREE(host);
+    CONST char *host = str;
 #endif /* _WINDOWS_NSL */
-	    return HT_NOT_ACCEPTABLE; /* only HTDoConnect checks this. */
-	}
+#ifdef NSL_FORK
+    /* for transfer of result between from child to parent: */
+    static struct {
+	struct hostent	h;
+	char		rest[REHOSTENT_SIZE];
+    } 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
+     */
+    char *rehostent = (char *)&aligned_full_rehostent;
+
+    /* for transfer of status from child to parent: */
+    struct _statuses {
+	size_t rehostentlen;
+	int h_length;
+	int child_errno;  /* maybe not very useful */
+	int child_h_errno;
+	BOOL h_errno_valid;
+    } statuses;
+
+    size_t rehostentlen = 0;
+#endif /* NSL_FORK */
+
+    struct hostent *result_phost = NULL;
+
+    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;
+	h_errno = NO_RECOVERY;
+	return NULL;
+    }
+
+#ifdef _WINDOWS_NSL
+    strncpy(host, str, (size_t)512);
+#else
+    host = str;
+#endif /*  _WINDOWS_NSL */
 
 #ifdef MVS	/* Outstanding problem with crash in MVS gethostbyname */
-	CTRACE(tfp, "HTParseInet: Calling gethostbyname(%s)\n", host);
+    CTRACE(tfp, "LYGetHostByName: Calling gethostbyname(%s)\n", host);
 #endif /* MVS */
 
+    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 success = 0;
-	    /*
-	    **	Pipe, child pid, status buffers, cycle count, select()
-	    **	control variables.
+    {
+	int got_rehostent = 0;
+	/*
+	**	Pipe, child pid, status buffers, cycle count, select()
+	**	control variables.
 	    */
-	    pid_t fpid, waitret;
-	    int pfd[2], h_length, selret, readret, waitstat = 0, cycle = 0;
-	    fd_set readfds;
-	    struct timeval timeout;
-	    int dns_patience = 30; /* how many seconds will we wait for DNS? */
-	    int child_exited = 0;
+	pid_t fpid, waitret;
+	int pfd[2], selret, readret, waitstat = 0, cycle = 0;
+	fd_set readfds;
+	struct timeval timeout;
+	int dns_patience = 30; /* how many seconds will we wait for DNS? */
+	int child_exited = 0;
 
 	    /*
 	    **  Reap any children that have terminated since last time
@@ -524,113 +713,135 @@ PUBLIC int HTParseInet ARGS2(
 	    **  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;
+	do {
+	    waitret = waitpid(-1, 0, WNOHANG);
+	} while (waitret > 0 || (waitret == -1 && errno == EINTR));
+	waitret = 0;
 
-	    pipe(pfd);
+	pipe(pfd);
 
-	    if ((fpid = fork()) == 0 ) {
-		struct hostent  *phost;	/* Pointer to host - See netdb.h */
-		/*
-		**  Child - for the long call.
+	if ((fpid = fork()) == 0 ) {
+	    struct 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);
+	    (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);
+	    (void) signal(SIGINT, quench);
 #ifndef NOSIGHUP
-		(void) signal(SIGHUP, quench);
+	    (void) signal(SIGHUP, quench);
 #endif /* NOSIGHUP */
 #ifdef SIGTSTP
-		if (no_suspend)
-		    (void) signal(SIGTSTP, SIG_IGN);
-		else
-		    (void) signal(SIGTSTP, SIG_DFL);
+	    if (no_suspend)
+		(void) signal(SIGTSTP, SIG_IGN);
+	    else
+		(void) signal(SIGTSTP, SIG_DFL);
 #endif /* SIGTSTP */
 #ifdef SIGWINCH
-		(void) signal(SIGWINCH, SIG_IGN);
+	    (void) signal(SIGWINCH, SIG_IGN);
 #endif /* SIGWINCH */
 #ifndef __linux__
 #ifndef DOSPATH
-		signal(SIGBUS, SIG_DFL);
+	    signal(SIGBUS, SIG_DFL);
 #endif /* DOSPATH */
 #endif /* !__linux__ */
-		signal(SIGSEGV, SIG_DFL);
-		signal(SIGILL, SIG_DFL);
+	    signal(SIGSEGV, SIG_DFL);
+	    signal(SIGILL, SIG_DFL);
 
-		/*
+	    /*
 		**  Child won't use read side.  -BL
 		*/
-		close(pfd[0]);
-		phost = gethostbyname(host);
+	    close(pfd[0]);
+	    phost = gethostbyname(host);
+	    statuses.child_h_errno = h_errno;
+	    statuses.h_errno_valid = YES;
 #ifdef MVS
-		CTRACE(tfp, "HTParseInet: gethostbyname() returned %d\n", phost);
+	    CTRACE(tfp, "LYGetHostByName: gethostbyname() returned %d\n", phost);
 #endif /* MVS */
 
-		/*
-		**  Send length of subsequent value to parent (as a
-		**  native int).
-		*/
-		if (OK_HOST(phost))
-			h_length = phost->h_length;
-		else
-			h_length = 0;
-		write(pfd[1], &h_length, sizeof h_length);
-
-		if (h_length) {
-		    /*
-		    **  Return value through pipe...
-		    */
-		    write(pfd[1], phost->h_addr, phost->h_length);
-		    _exit(0);
-		} else {
-		    /*
-		    **  ... or return error as exit code.
-		    */
-		    _exit(1);
-		}
+#ifdef DEBUG_HOSTENT_CHILD
+	    dump_hostent("CHILD gethostbyname", phost);
+#endif
+	    if (OK_HOST(phost)) {
+		rehostentlen = fill_rehostent(rehostent, REHOSTENT_SIZE, phost);
+#ifdef DEBUG_HOSTENT_CHILD
+		dump_hostent("CHILD fill_rehostent", (struct hostent *)rehostent);
+#endif
+	    }
+	    if (rehostentlen <= sizeof(struct hostent) ||
+		!OK_HOST((struct hostent *)rehostent)) {
+		rehostentlen = 0;
+		statuses.h_length = 0;
+	    } else {
+		statuses.h_length = ((struct hostent *)rehostent)->h_length;
 	    }
-
 	    /*
-	    **	(parent) Wait until lookup finishes, or interrupt,
-	    **	or cycled too many times (just in case) -BL
+	    **  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).
 	    */
+	    statuses.child_errno = errno;
+	    statuses.rehostentlen = rehostentlen;
+	    write(pfd[1], &statuses, sizeof(statuses));
 
-	    close(pfd[1]);      /* parent won't use write side -BL */
 
-	    while (cycle < dns_patience) {
+	    if (rehostentlen) {
 		/*
-		**  Avoid infinite loop in the face of the unexpected.  -BL
+		**  Return our resulting rehostent through pipe...
 		*/
-		cycle++;
-
-		FD_ZERO(&readfds);
-#ifndef USE_SLANG
+		write(pfd[1], rehostent, rehostentlen);
+		_exit(0);
+	    } else {
 		/*
-		**  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
+		**  ... or return error as exit code.
+		*/
+		_exit(1);
+	    }
+	}
+
+	/*
+	**	(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 */
+
+	while (cycle < dns_patience) {
+	    /*
+		**  Avoid infinite loop in the face of the unexpected.  -BL
 		*/
-		if (isatty(fileno(stdin))) FD_SET(fileno(stdin), &readfds);
-#endif /* USE_SLANG */
-		timeout.tv_sec = 1;
-		timeout.tv_usec = 0;
-		FD_SET(pfd[0], &readfds);
+	    cycle++;
+
+	    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);
+		}
+	    }
+
+	    timeout.tv_sec = 1;
+	    timeout.tv_usec = 0;
+	    FD_SET(pfd[0], &readfds);
 
 		/*
 		**  Return when data received, interrupted, or failed.
@@ -638,107 +849,117 @@ PUBLIC int HTParseInet ARGS2(
 		**  select(), to be nice to the system.  -BL
 		*/
 #ifdef SOCKS
-		if (socks_flag)
-		    selret = Rselect(pfd[0] + 1, (void *)&readfds, NULL, NULL, &timeout);
-		else
+	    if (socks_flag)
+		selret = Rselect(pfd[0] + 1, (void *)&readfds, NULL, NULL, &timeout);
+	    else
 #endif /* SOCKS */
-		    selret = select(pfd[0] + 1, (void *)&readfds, NULL, NULL, &timeout);
+		selret = select(pfd[0] + 1, (void *)&readfds, NULL, NULL, &timeout);
 
-		if ((selret > 0) && FD_ISSET(pfd[0], &readfds)) {
-		    /*
-		    **	First get length of address.  -BL
-		    */
-		    readret = read(pfd[0], (void *)&h_length, sizeof h_length);
-		    if (readret == sizeof h_length &&
-			h_length == sizeof soc_in->sin_addr) {
+	    if ((selret > 0) && FD_ISSET(pfd[0], &readfds)) {
+		/*
+		**	First get status, including length of address.  -BL, kw
+		*/
+		readret = read(pfd[0], &statuses, sizeof(statuses));
+		if (readret == sizeof(statuses)) {
+		    h_errno = statuses.child_h_errno;
+		    errno = statuses.child_errno;
+		    if (statuses.h_errno_valid)
+			lynx_nsl_status = HT_H_ERRNO_VALID;
+		    if (statuses.rehostentlen > sizeof(struct hostent)) {
 			/*
-			**  Then get address itself.  -BL
+			**  Then get the full reorganized hostent.  -BL, kw
 			*/
-			readret = read(pfd[0], (void *)&soc_in->sin_addr, h_length);
-			if (readret == h_length) success = 1;
+			readret = read(pfd[0], rehostent, statuses.rehostentlen);
+#ifdef DEBUG_HOSTENT
+			dump_hostent("Read from pipe", (struct hostent *)rehostent);
+#endif
+			if (readret == (int) statuses.rehostentlen) {
+			    got_rehostent = 1;
+			    result_phost = (struct 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;
-	    	}
+		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) {
+		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;
-		    cycle--;
-		    continue;
-		}
-
+	    if ((waitret = waitpid(fpid, &waitstat, WNOHANG)) > 0) {
 		/*
-		**  Abort if interrupt key pressed.
+		**	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
 		*/
-		if (HTCheckForInterrupt()) {
-		    CTRACE(tfp, "HTParseInet: INTERRUPTED gethostbyname.\n");
-		    kill(fpid, SIGTERM);
-		    waitpid(fpid, NULL, WNOHANG);
-		    FREE(host);
-		    close(pfd[0]);
-		    return HT_INTERRUPTED;
-		}
+		child_exited = 1;
+		cycle--;
+		continue;
 	    }
-	    close(pfd[0]);
-	    if (waitret <= 0) {
+
+	    /*
+	    **  Abort if interrupt key pressed.
+	    */
+	    if (HTCheckForInterrupt()) {
+		CTRACE(tfp, "LYGetHostByName: INTERRUPTED gethostbyname.\n");
 		kill(fpid, SIGTERM);
-		waitret = waitpid(fpid, &waitstat, WNOHANG);
+		waitpid(fpid, NULL, WNOHANG);
+		close(pfd[0]);
+		lynx_nsl_status = HT_INTERRUPTED;
+		return NULL;
 	    }
-	    if (waitret > 0) {
-		if (WIFEXITED(waitstat)) {
-		    CTRACE(tfp, "HTParseInet: NSL_FORK child %d exited, status 0x%x.\n",
-				(int)waitret, waitstat);
-		} else if (WIFSIGNALED(waitstat)) {
-		    CTRACE(tfp, "HTParseInet: NSL_FORK child %d got signal, status 0x%x!\n",
-				(int)waitret, waitstat);
+	}
+	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, waitstat);
+	    } else if (WIFSIGNALED(waitstat)) {
+		CTRACE(tfp, "LYGetHostByName: NSL_FORK child %d got signal, status 0x%x!\n",
+		       (int)waitret, waitstat);
 #ifdef WCOREDUMP
-		    if (WCOREDUMP(waitstat)) {
-			CTRACE(tfp, "HTParseInet: NSL_FORK child %d dumped core!\n",
-				    (int)waitret);
-		    }
-#endif /* WCOREDUMP */
-		} else if (WIFSTOPPED(waitstat)) {
-		    CTRACE(tfp, "HTParseInet: NSL_FORK child %d is stopped, status 0x%x!\n",
-				(int)waitret, waitstat);
+		if (WCOREDUMP(waitstat)) {
+		    CTRACE(tfp, "LYGetHostByName: NSL_FORK child %d dumped core!\n",
+			   (int)waitret);
 		}
-	    }
-	    if (!success) {
-		memset((void *)&soc_in->sin_addr, 0, sizeof(soc_in->sin_addr));
-		goto failed;
+#endif /* WCOREDUMP */
+	    } else if (WIFSTOPPED(waitstat)) {
+		CTRACE(tfp, "LYGetHostByName: NSL_FORK child %d is stopped, status 0x%x!\n",
+		       (int)waitret, waitstat);
 	    }
 	}
-#else /* Not NSL_FORK: */
-#ifdef DJGPP
-	soc_in->sin_addr.s_addr = htonl(resolve(host));
-	if (soc_in->sin_addr.s_addr == 0) {
+	if (!got_rehostent) {
 	    goto failed;
 	}
-#else /* !NSL_FORK, !DJGPP: */
+    }
+#else /* Not NSL_FORK: */
+
 #ifdef _WINDOWS_NSL
-	{
+    {
 #ifdef __BORLANDC__
 		HANDLE hThread, dwThreadID;
 #else
@@ -765,18 +986,197 @@ PUBLIC int HTParseInet ARGS2(
 			  if (!CloseHandle(hThread))
 				 MessageBox((void *)NULL, "CloseHandle","CloseHandle Failed",
 						0L);
-			  return HT_INTERRUPTED;
+			  lynx_nsl_status = HT_INTERRUPTED;
+			  return NULL;
 			};
-	};
+		if (phost) {
+		    lynx_nsl_status = HT_OK;
+		    result_phost = phost;
+		} else {
+		    lynx_nsl_status = HT_ERROR;
+		    goto failed;
+		}
+    };
+
+#else /* !NSL_FORK, !_WINDOWS_NSL: */
+    {
+	struct 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;
+}
+
+#endif /* from here on DJGPP joins us again. */
+
+
+/*	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.
+*/
+PUBLIC int HTParseInet ARGS2(
+	SockA *,	soc_in,
+	CONST char *,	str)
+{
+    char *port;
+    int dotcount_ip = 0;	/* for dotted decimal IP addr */
+#ifndef _WINDOWS_NSL
+    char *host = NULL;
+#endif /* _WINDOWS_NSL */
+
+    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;
+    }
+#ifdef _WINDOWS_NSL
+    strncpy(host, str, (size_t)512);
+#else
+    StrAllocCopy(host, str);	/* Make a copy we can mutilate */
+#endif /*  _WINDOWS_NSL */
+    /*
+    **	Parse port number if present.
+    */
+    if ((port = strchr(host, ':')) != NULL) {
+	*port++ = 0;		/* Chop off port */
+	if (port[0] >= '0' && port[0] <= '9') {
+#ifdef unix
+	    soc_in->sin_port = htons(atol(port));
+#else /* VMS: */
+#ifdef DECNET
+	    soc_in->sdn_objnum = (unsigned char)(strtol(port, (char**)0, 10));
+#else
+	    soc_in->sin_port = htons((unsigned short)strtol(port,(char**)0,10));
+#endif /* Decnet */
+#endif /* Unix vs. VMS */
+#ifdef SUPPRESS 	/* 1. crashes!?!.  2. Not recommended */
+	} 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 */
+	}
+    }
+
+#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: */
+	char *strptr = host;
+	while (*strptr) {
+	    if (*strptr == '.') {
+		dotcount_ip++;
+	    } else if (!isdigit(*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 DJGPP
+	soc_in->sin_addr.s_addr = htonl(aton(host));
+#else
+#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);
+#ifndef _WINDOWS_NSL
+	    FREE(host);
+#endif /* _WINDOWS_NSL */
+	    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 */
+#endif /* DJGPP */
+#ifndef _WINDOWS_NSL
+	FREE(host);
+#endif /* _WINDOWS_NSL */
+    } else {		    /* Alphanumeric node name: */
+
+#ifdef MVS	/* Outstanding problem with crash in MVS gethostbyname */
+	CTRACE(tfp, "HTParseInet: Calling LYGetHostByName(%s)\n", host);
+#endif /* MVS */
+
+#ifdef DJGPP
+	if (!valid_hostname(host)) {
+	    FREE(host);
+	    return HT_NOT_ACCEPTABLE; /* only HTDoConnect checks this. */
+	}
+	soc_in->sin_addr.s_addr = htonl(resolve(host));
+	if (soc_in->sin_addr.s_addr == 0) {
+	    goto failed;
+	}
+#else /* !DJGPP: */
+#ifdef _WINDOWS_NSL
+	phost = LYGetHostByName(host);	/* See above */
 	if (!phost) goto failed;
 	memcpy((void *)&soc_in->sin_addr, phost->h_addr, phost->h_length);
-#else /* !NSL_FORK, !DJGPP, !_WINDOWS_NSL: */
+#else /* !DJGPP, !_WINDOWS_NSL: */
 	{
 	    struct hostent  *phost;
-	    phost = gethostbyname(host);	/* See netdb.h */
-#ifdef MVS
-	    CTRACE(tfp, "HTParseInet: gethostbyname() returned %d\n", phost);
-#endif /* MVS */
+	    phost = LYGetHostByName(host);	/* See above */
+
 	    if (!phost) goto failed;
 #if defined(VMS) && defined(CMU_TCP)
 	    /*
@@ -789,17 +1189,20 @@ PUBLIC int HTParseInet ARGS2(
 	    */
 	    memcpy((void *)&soc_in->sin_addr, phost->h_addr, 4);
 #else
+	    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, phost->h_length);
 #endif /* VMS && CMU_TCP */
 	}
-#endif /* !NSL_FORK, !DJGPP, !_WINDOWS_NSL */
-#endif /* !NSL_FORK, !DJGPP */
-#endif /* !NSL_FORK */
+#endif /* !DJGPP, !_WINDOWS_NSL */
+#endif /* !DJGPP */
 #ifndef _WINDOWS_NSL
 	FREE(host);
 #endif /* _WINDOWS_NSL */
 
-    }
+    }	/* Alphanumeric node name */
 
     CTRACE(tfp, "HTParseInet: Parsed address as port %d, IP address %d.%d.%d.%d\n",
 		(int)ntohs(soc_in->sin_port),
@@ -817,8 +1220,14 @@ failed:
 #ifndef _WINDOWS_NSL
     FREE(host);
 #endif /* _WINDOWS_NSL */
+    switch (lynx_nsl_status) {
+    case HT_NOT_ACCEPTABLE:
+    case HT_INTERRUPTED:
+	return lynx_nsl_status;
+    default:
     return -1;
 }
+}
 
 /*	Free our name for the host on which we are - FM
 **	-------------------------------------------