summaryrefslogtreecommitdiffstats
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.c2623
1 files changed, 2623 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c
new file mode 100644
index 0000000..a3bfda6
--- /dev/null
+++ b/WWW/Library/Implementation/HTTCP.c
@@ -0,0 +1,2623 @@
+/*
+ * $LynxId: HTTCP.c,v 1.163 2022/04/01 23:18:35 Rajeev.V.Pillai 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
+ */
+
+#define LYNX_ADDRINFO struct addrinfo
+#define LYNX_HOSTENT struct hostent
+
+#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>
+#define FREE_NSL_FORK(p) { FREE(p); }
+#elif defined(_WINDOWS_NSL)
+#define FREE_NSL_FORK(p) if ((p) == gbl_phost) { FREE(p); }
+#else
+#define FREE_NSL_FORK(p) /* nothing */
+#endif /* NSL_FORK */
+
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+#ifdef __DJGPP__
+#include <netdb.h>
+#endif /* __DJGPP__ */
+
+#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(LY_SOCKADDR * soc_A)
+{
+#ifdef INET6
+ static char hostbuf[MAXHOSTNAMELEN];
+ struct sockaddr *soc_addr = &(soc_A->soc_address);
+
+ getnameinfo(soc_addr,
+ SA_LEN(soc_addr),
+ hostbuf, (socklen_t) sizeof(hostbuf),
+ NULL, 0,
+ NI_NUMERICHOST);
+ return hostbuf;
+#else
+ struct sockaddr_in *soc_in = &(soc_A->soc_in);
+ 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'));
+}
+
+/* for transfer of status from child to parent: */
+typedef struct _statuses {
+ size_t rehostentlen;
+ int h_length;
+ int child_errno; /* sometimes useful to pass this on */
+ int child_h_errno;
+ BOOL h_errno_valid;
+} STATUSES;
+
+/*
+ * Function to allow us to be killed with a normal signal (not
+ * SIGKILL), but don't go through normal libc exit() processing, which
+ * would screw up parent's stdio. -BL
+ */
+#ifdef NSL_FORK
+static void quench(int sig GCC_UNUSED)
+{
+ _exit(2);
+}
+#endif
+
+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 */
+
+/*
+ * 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 void *data)
+{
+ if (TRACE) {
+ int i;
+ char **pcnt;
+ const LYNX_HOSTENT *phost = data;
+
+ CTRACE((tfp, "%s: %p ", msgprefix, (const void *) phost));
+ if (phost) {
+ CTRACE((tfp, "{ h_name = %p", (void *) 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 ? " " : ", "),
+ (void *) *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 ? "" : ","),
+ (void *) *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);
+ }
+}
+
+#ifdef NSL_FORK
+
+/*
+ * Even though it is a small amount, we cannot count on reading the whole
+ * struct via a pipe in one read -TD
+ */
+static unsigned read_bytes(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;
+}
+
+static unsigned read_hostent(int fd, char *buffer, size_t length)
+{
+ unsigned have = read_bytes(fd, buffer, length);
+
+ if (have) {
+ LYNX_HOSTENT *data = (LYNX_HOSTENT *) (void *) buffer;
+ char *next_char = (char *) data + sizeof(*data);
+ char **next_ptr = (char **) (void *) next_char;
+ long offset = 0;
+ int n;
+ int num_addrs = 0;
+ int num_aliases = 0;
+
+ if (data->h_addr_list) {
+ data->h_addr_list = next_ptr;
+ while (next_ptr[num_addrs] != 0) {
+ ++num_addrs;
+ }
+ next_ptr += (num_addrs + 1);
+ next_char += (size_t) (num_addrs + 1) * sizeof(data->h_addr_list[0]);
+ }
+
+ if (data->h_aliases) {
+ data->h_aliases = next_ptr;
+ while (next_ptr[num_aliases] != 0) {
+ ++num_aliases;
+ }
+ next_char += (size_t) (num_aliases + 1) * sizeof(data->h_aliases[0]);
+ }
+
+ if (data->h_name) {
+ offset = next_char - data->h_name;
+ data->h_name = next_char;
+ } else if (data->h_addr_list) {
+ offset = next_char - (char *) data->h_addr_list[0];
+ } else if (data->h_aliases) {
+ offset = next_char - (char *) data->h_aliases[0];
+ }
+
+ if (data->h_addr_list) {
+ for (n = 0; n < num_addrs; ++n) {
+ data->h_addr_list[n] += offset;
+ }
+ }
+
+ if (data->h_aliases) {
+ for (n = 0; n < num_aliases; ++n) {
+ data->h_aliases[n] += offset;
+ }
+ }
+ }
+
+ return have;
+}
+#endif /* NSL_FORK */
+
+/*
+ * 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(void **rehostent,
+ const LYNX_HOSTENT *phost)
+{
+ static const char *this_func = "fill_rehostent";
+
+ LYNX_HOSTENT *data = 0;
+ int num_addrs = 0;
+ int num_aliases = 0;
+ char *result = 0;
+ char *p_next_char;
+ char **p_next_charptr;
+ size_t name_len = 0;
+ size_t need = sizeof(LYNX_HOSTENT);
+ int n;
+
+ if (!phost)
+ return 0;
+
+ if (phost->h_name) {
+ name_len = strlen(phost->h_name);
+ need += name_len + 1;
+ }
+ if (phost->h_addr_list) {
+ while (phost->h_addr_list[num_addrs]) {
+ num_addrs++;
+ }
+ need += ((size_t) num_addrs + 1) * ((size_t) phost->h_length
+ + sizeof(phost->h_addr_list[0]));
+ }
+ if (phost->h_aliases) {
+ while (phost->h_aliases[num_aliases]) {
+ need += strlen(phost->h_aliases[num_aliases]) + 1;
+ num_aliases++;
+ }
+ need += ((size_t) num_aliases + 1) * sizeof(phost->h_aliases[0]);
+ }
+
+ if ((result = calloc(need, sizeof(char))) == 0)
+ outofmem(__FILE__, this_func);
+
+ *rehostent = result;
+
+ data = (LYNX_HOSTENT *) (void *) result;
+
+ data->h_addrtype = phost->h_addrtype;
+ data->h_length = phost->h_length;
+
+ p_next_char = result + sizeof(LYNX_HOSTENT);
+
+ p_next_charptr = (char **) (void *) p_next_char;
+ 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_name) {
+ data->h_name = p_next_char;
+ strcpy(p_next_char, phost->h_name);
+ p_next_char += name_len + 1;
+ }
+
+ if (phost->h_addr_list) {
+ data->h_addr_list = p_next_charptr;
+ for (n = 0; n < num_addrs; ++n) {
+ MemCpy(p_next_char, phost->h_addr_list[n], phost->h_length);
+ *p_next_charptr++ = p_next_char;
+ p_next_char += phost->h_length;
+ }
+ ++p_next_charptr;
+ }
+
+ if (phost->h_aliases) {
+ data->h_aliases = p_next_charptr;
+ for (n = 0; n < num_aliases; ++n) {
+ strcpy(p_next_char, phost->h_aliases[n]);
+ *p_next_charptr++ = p_next_char;
+ p_next_char += strlen(phost->h_aliases[n]) + 1;;
+ }
+ }
+ return need;
+}
+#endif /* NSL_FORK */
+
+/*
+ * 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((void **) &rehostent, 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 */
+
+#ifdef 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
+
+static BOOL setup_nsl_fork(void (*really) (const char *,
+ const char *,
+ STATUSES *,
+ void **),
+ unsigned (*readit) (int, char *, size_t),
+ void (*dumpit) (const char *, const void *),
+ const char *host,
+ const char *port,
+ void **rehostent)
+{
+ static const char *this_func = "setup_nsl_fork";
+
+ STATUSES statuses;
+
+ /*
+ * fork-based gethostbyname() with checks for interrupts.
+ * - Tom Zerucha (tz@execpc.com) & FM
+ */
+ int got_rehostent = 0;
+
+#if HAVE_SIGACTION
+ sigset_t old_sigset;
+ sigset_t new_sigset;
+#endif
+ /*
+ * Pipe, child pid, status buffers, start time, select() control
+ * variables.
+ */
+ int fpid, waitret;
+ int pfd[2], selret;
+ unsigned readret;
+
+#ifdef HAVE_TYPE_UNIONWAIT
+ union wait waitstat;
+
+#else
+ int waitstat = 0;
+#endif
+ time_t start_time = time((time_t *) 0);
+ fd_set readfds;
+ struct timeval one_second;
+ long dns_patience = 30; /* how many seconds will we wait for DNS? */
+ int child_exited = 0;
+
+ memset(&statuses, 0, sizeof(STATUSES));
+ statuses.h_errno_valid = NO;
+
+ /*
+ * Reap any children that have terminated since last time through.
+ * This might include children that we killed, then waited with WNOHANG
+ * before they were actually ready to be reaped. (Should be max of 1
+ * in this state, but the loop is safe if waitpid() is implemented
+ * correctly: returns 0 when children exist but none have exited; -1
+ * with errno == ECHILD when no children.) -BL
+ */
+ do {
+ waitret = waitpid(-1, 0, WNOHANG);
+ } while (waitret > 0 || (waitret == -1 && errno == EINTR));
+ waitret = 0;
+
+ IGNORE_RC(pipe(pfd));
+
+#if HAVE_SIGACTION
+ /*
+ * Attempt to prevent a rare situation where the child could execute
+ * the Lynx signal handlers because it gets killed before it even has a
+ * chance to reset its handlers, resulting in bogus 'Exiting via
+ * interrupt' message and screen corruption or worse.
+ * Should that continue to be reported, for systems without
+ * sigprocmask(), we need to find a different solutions for those. -
+ * kw 19990430
+ */
+ sigemptyset(&new_sigset);
+ sigaddset(&new_sigset, SIGTERM);
+ sigaddset(&new_sigset, SIGINT);
+#ifndef NOSIGHUP
+ sigaddset(&new_sigset, SIGHUP);
+#endif /* NOSIGHUP */
+#ifdef SIGTSTP
+ sigaddset(&new_sigset, SIGTSTP);
+#endif /* SIGTSTP */
+#ifdef SIGWINCH
+ sigaddset(&new_sigset, SIGWINCH);
+#endif /* SIGWINCH */
+ sigprocmask(SIG_BLOCK, &new_sigset, &old_sigset);
+#endif /* HAVE_SIGACTION */
+
+ if ((fpid = fork()) == 0) {
+ /*
+ * Child - for the long call.
+ *
+ * Make sure parent can kill us at will. -BL
+ */
+ (void) signal(SIGTERM, quench);
+
+ /*
+ * Also make sure the child does not run one of the signal handlers
+ * that may have been installed by Lynx if one of those signals
+ * occurs. For example we don't want the child to remove temp
+ * files on ^C, let the parent deal with that. - kw
+ */
+ (void) signal(SIGINT, quench);
+#ifndef NOSIGHUP
+ (void) signal(SIGHUP, quench);
+#endif /* NOSIGHUP */
+#ifdef SIGTSTP
+ if (no_suspend)
+ (void) signal(SIGTSTP, SIG_IGN);
+ else
+ (void) signal(SIGTSTP, SIG_DFL);
+#endif /* SIGTSTP */
+#ifdef SIGWINCH
+ (void) signal(SIGWINCH, SIG_IGN);
+#endif /* SIGWINCH */
+#ifndef __linux__
+#ifndef DOSPATH
+ signal(SIGBUS, SIG_DFL);
+#endif /* DOSPATH */
+#endif /* !__linux__ */
+ signal(SIGSEGV, SIG_DFL);
+ signal(SIGILL, SIG_DFL);
+
+#if HAVE_SIGACTION
+ /* Restore signal mask to whatever it was before the fork. -kw */
+ sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+#endif /* HAVE_SIGACTION */
+
+ /*
+ * Child won't use read side. -BL
+ */
+ close(pfd[0]);
+#ifdef HAVE_H_ERRNO
+ /* to detect cases when it doesn't get set although it should */
+ h_errno = -2;
+#endif
+ set_errno(0);
+ really(host, port, &statuses, rehostent);
+ /*
+ * Send variables indicating status of lookup to parent. That
+ * includes rehostentlen, which the parent will use as the size for
+ * the second read (if > 0).
+ */
+ if (!statuses.child_errno)
+ statuses.child_errno = errno;
+ IGNORE_RC(write(pfd[1], &statuses, sizeof(statuses)));
+
+ if (statuses.rehostentlen) {
+ /*
+ * Return our resulting rehostent through pipe...
+ */
+ IGNORE_RC(write(pfd[1], *rehostent, statuses.rehostentlen));
+ close(pfd[1]);
+ _exit(0);
+ } else {
+ /*
+ * ... or return error as exit code.
+ */
+ _exit(1);
+ }
+ }
+#if HAVE_SIGACTION
+ /*
+ * (parent) Restore signal mask to whatever it was before the fork. -
+ * kw
+ */
+ sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+#endif /* HAVE_SIGACTION */
+
+ /*
+ * (parent) Wait until lookup finishes, or interrupt, or cycled too
+ * many times (just in case) -BL
+ */
+
+ close(pfd[1]); /* parent won't use write side -BL */
+
+ if (fpid < 0) { /* fork failed */
+ close(pfd[0]);
+ goto failed;
+ }
+
+ while (child_exited || (long) (time((time_t *) 0) - start_time) < dns_patience) {
+
+ FD_ZERO(&readfds);
+ /*
+ * This allows us to abort immediately, not after 1-second timeout,
+ * when user hits abort key. Can't do this when using SLANG (or at
+ * least I don't know how), so SLANG users must live with up-to-1s
+ * timeout. -BL
+ *
+ * Whoops -- we need to make sure stdin is actually selectable!
+ * /dev/null isn't, on some systems, which makes some useful Lynx
+ * invocations fail. -BL
+ */
+ {
+ int kbd_fd = LYConsoleInputFD(TRUE);
+
+ if (kbd_fd != INVSOC) {
+ FD_SET(kbd_fd, &readfds);
+ }
+ }
+
+ one_second.tv_sec = 1;
+ one_second.tv_usec = 0;
+ FD_SET(pfd[0], &readfds);
+
+ /*
+ * Return when data received, interrupted, or failed. If nothing
+ * is waiting, we sleep for 1 second in select(), to be nice to the
+ * system. -BL
+ */
+#ifdef SOCKS
+ if (socks_flag)
+ selret = Rselect(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
+ else
+#endif /* SOCKS */
+ selret = select(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
+
+ if ((selret > 0) && FD_ISSET(pfd[0], &readfds)) {
+ /*
+ * First get status, including length of address. -BL, kw
+ */
+ readret = read_bytes(pfd[0], (char *) &statuses, sizeof(statuses));
+ if (readret == sizeof(statuses)) {
+ h_errno = statuses.child_h_errno;
+ set_errno(statuses.child_errno);
+#ifdef HAVE_H_ERRNO
+ if (statuses.h_errno_valid) {
+ lynx_nsl_status = HT_H_ERRNO_VALID;
+ /*
+ * If something went wrong in the child process other
+ * than normal lookup errors, and it appears that we
+ * have enough info to know what went wrong, generate
+ * diagnostic output. ENOMEM observed on linux in
+ * processes constrained with ulimit. It would be too
+ * unkind to abort the session, access to local files
+ * or through a proxy may still work. - kw
+ */
+ if (
+#ifdef NETDB_INTERNAL /* linux glibc: defined in netdb.h */
+ (errno && h_errno == NETDB_INTERNAL) ||
+#endif
+ (errno == ENOMEM &&
+ statuses.rehostentlen == 0 &&
+ /* should probably be NETDB_INTERNAL if child
+ memory exhausted, but we may find that
+ h_errno remains unchanged. - kw */
+ h_errno == -2)) {
+#ifndef MULTINET
+ HTInetStatus("CHILD gethostbyname");
+#endif
+ HTAlert(LYStrerror(statuses.child_errno));
+ if (errno == ENOMEM) {
+ /*
+ * Not much point in continuing, right? Fake a
+ * 'z', should shorten pointless guessing
+ * cycle. - kw
+ */
+ LYFakeZap(YES);
+ }
+ }
+ }
+#endif /* HAVE_H_ERRNO */
+ if (statuses.rehostentlen != 0) {
+ /*
+ * Then get the full reorganized hostent. -BL, kw
+ */
+ if ((*rehostent = malloc(statuses.rehostentlen)) == 0)
+ outofmem(__FILE__, this_func);
+ readret = (*readit) (pfd[0], *rehostent, statuses.rehostentlen);
+#ifdef DEBUG_HOSTENT
+ dumpit("Read from pipe", *rehostent);
+#endif
+ if (readret == statuses.rehostentlen) {
+ got_rehostent = 1;
+ lynx_nsl_status = HT_OK;
+ } else if (!statuses.h_errno_valid) {
+ lynx_nsl_status = HT_INTERNAL;
+ }
+ }
+ } else {
+ lynx_nsl_status = HT_ERROR;
+ }
+ /*
+ * Make sure child is cleaned up. -BL
+ */
+ if (!child_exited)
+ waitret = waitpid(fpid, &waitstat, WNOHANG);
+ if (!WIFEXITED(waitstat) && !WIFSIGNALED(waitstat)) {
+ kill(fpid, SIGTERM);
+ waitret = waitpid(fpid, &waitstat, WNOHANG);
+ }
+ break;
+ }
+
+ /*
+ * Clean up if child exited before & no data received. -BL
+ */
+ if (child_exited) {
+ waitret = waitpid(fpid, &waitstat, WNOHANG);
+ break;
+ }
+ /*
+ * If child exited, loop once more looking for data. -BL
+ */
+ if ((waitret = waitpid(fpid, &waitstat, WNOHANG)) > 0) {
+ /*
+ * Data will be arriving right now, so make sure we don't
+ * short-circuit out for too many loops, and skip the interrupt
+ * check. -BL
+ */
+ child_exited = 1;
+ continue;
+ }
+
+ /*
+ * Abort if interrupt key pressed.
+ */
+ if (HTCheckForInterrupt()) {
+ CTRACE((tfp, "%s: INTERRUPTED gethostbyname.\n", this_func));
+ kill(fpid, SIGTERM);
+ waitpid(fpid, NULL, WNOHANG);
+ close(pfd[0]);
+ lynx_nsl_status = HT_INTERRUPTED;
+ return FALSE;
+ }
+ }
+ close(pfd[0]);
+ if (waitret <= 0) {
+ kill(fpid, SIGTERM);
+ waitret = waitpid(fpid, &waitstat, WNOHANG);
+ }
+ if (waitret > 0) {
+ if (WIFEXITED(waitstat)) {
+ CTRACE((tfp,
+ "%s: NSL_FORK child %d exited, status 0x%x.\n",
+ this_func, (int) waitret, WEXITSTATUS(waitstat)));
+ } else if (WIFSIGNALED(waitstat)) {
+ CTRACE((tfp,
+ "%s: NSL_FORK child %d got signal, status 0x%x!\n",
+ this_func, (int) waitret, WTERMSIG(waitstat)));
+#ifdef WCOREDUMP
+ if (WCOREDUMP(waitstat)) {
+ CTRACE((tfp,
+ "%s: NSL_FORK child %d dumped core!\n",
+ this_func, (int) waitret));
+ }
+#endif /* WCOREDUMP */
+ } else if (WIFSTOPPED(waitstat)) {
+ CTRACE((tfp,
+ "%s: NSL_FORK child %d is stopped, status 0x%x!\n",
+ this_func, (int) waitret, WSTOPSIG(waitstat)));
+ }
+ }
+ if (!got_rehostent) {
+ goto failed;
+ }
+ return TRUE;
+ failed:
+ return FALSE;
+}
+
+/*
+ * This is called via the child-side of the fork.
+ */
+static void really_gethostbyname(const char *host,
+ const char *port GCC_UNUSED,
+ STATUSES * statuses,
+ void **rehostent)
+{
+ LYNX_HOSTENT *phost; /* Pointer to host - See netdb.h */
+ LYNX_HOSTENT *result = 0;
+
+ (void) port;
+
+ phost = gethostbyname(host);
+ statuses->rehostentlen = 0;
+ statuses->child_errno = errno;
+ statuses->child_h_errno = h_errno;
+#ifdef HAVE_H_ERRNO
+ statuses->h_errno_valid = YES;
+#endif
+#ifdef MVS
+ CTRACE((tfp, "really_gethostbyname() returned %d\n", phost));
+#endif /* MVS */
+
+#ifdef DEBUG_HOSTENT_CHILD
+ dump_hostent("CHILD gethostbyname", phost);
+#endif
+ if (OK_HOST(phost)) {
+ statuses->rehostentlen = fill_rehostent(rehostent, phost);
+ result = (LYNX_HOSTENT *) (*rehostent);
+#ifdef DEBUG_HOSTENT_CHILD
+ dump_hostent("CHILD fill_rehostent", result);
+#endif
+ }
+ if (statuses->rehostentlen <= sizeof(LYNX_HOSTENT) || !OK_HOST(result)) {
+ statuses->rehostentlen = 0;
+ statuses->h_length = 0;
+ } else {
+ statuses->h_length = result->h_length;
+#ifdef HAVE_H_ERRNO
+ if (h_errno == -2) /* success, but h_errno unchanged? */
+ statuses->h_errno_valid = NO;
+#endif
+ }
+}
+#endif /* NSL_FORK */
+
+/* Resolve an internet hostname, like gethostbyname
+ * ------------------------------------------------
+ *
+ * On entry,
+ * host 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
+ */
+static LYNX_HOSTENT *LYGetHostByName(char *host)
+{
+ static const char *this_func = "LYGetHostByName";
+
+#ifdef NSL_FORK
+ /* for transfer of result between from child to parent: */
+ LYNX_HOSTENT *rehostent = 0;
+#endif /* NSL_FORK */
+
+ LYNX_HOSTENT *result_phost = NULL;
+
+#ifdef __DJGPP__
+ _resolve_hook = ResolveYield;
+#endif
+
+ if (!host) {
+ CTRACE((tfp, "%s: Can't parse `NULL'.\n", this_func));
+ lynx_nsl_status = HT_INTERNAL;
+ return NULL;
+ }
+ CTRACE((tfp, "%s: parsing `%s'.\n", this_func, host));
+
+ /* Could disable this if all our callers already check - kw */
+ if (HTCheckForInterrupt()) {
+ CTRACE((tfp, "%s: INTERRUPTED for '%s'.\n", this_func, host));
+ 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, "%s: Calling gethostbyname(%s)\n", this_func, 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
+ if (!setup_nsl_fork(really_gethostbyname,
+ read_hostent,
+ dump_hostent,
+ host, NULL, (void **) &rehostent)) {
+ goto failed;
+ }
+ result_phost = rehostent;
+#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, "%s: gethostbyname() returned %d\n", this_func, 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(this_func, result_phost);
+ CTRACE((tfp, "%s: Resolved name to a hostent.\n", this_func));
+#endif
+
+ return result_phost; /* OK */
+
+ failed:
+ CTRACE((tfp, "%s: Can't find internet node name `%s'.\n", this_func, host));
+ return NULL;
+}
+
+BOOLEAN LYCheckHostByName(char *host)
+{
+ LYNX_HOSTENT *data = LYGetHostByName(host);
+ BOOLEAN result = (data != NULL);
+
+ FREE_NSL_FORK(data);
+ return result;
+}
+
+/* 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)
+{
+ static const char *this_func = "HTParseInet";
+
+ char *port;
+ int dotcount_ip = 0; /* for dotted decimal IP addr */
+ char *strptr;
+ char *host = NULL;
+
+ if (!str) {
+ CTRACE((tfp, "%s: Can't parse `NULL'.\n", this_func));
+ return -1;
+ }
+ CTRACE((tfp, "%s: parsing `%s'.\n", this_func, str));
+ if (HTCheckForInterrupt()) {
+ CTRACE((tfp, "%s: INTERRUPTED for '%s'.\n", this_func, 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 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 */
+ FREE(host);
+ } else { /* Alphanumeric node name: */
+
+#ifdef MVS /* Outstanding problem with crash in MVS gethostbyname */
+ CTRACE((tfp, "%s: Calling LYGetHostByName(%s)\n", this_func, 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);
+ FREE(gbl_phost);
+#else /* !_WINDOWS_NSL */
+ {
+ LYNX_HOSTENT *phost;
+
+ phost = LYGetHostByName(host); /* See above */
+
+ 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);
+ FREE_NSL_FORK(phost);
+ }
+#endif /* _WINDOWS_NSL */
+
+ FREE(host);
+ } /* Alphanumeric node name */
+
+ CTRACE((tfp,
+ "%s: Parsed address as port %d, IP address %d.%d.%d.%d\n",
+ this_func,
+ (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, "%s: Can't find internet node name `%s'.\n",
+ this_func, 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 void dump_addrinfo(const char *tag, const void *data)
+{
+ const LYNX_ADDRINFO *res;
+ int count = 0;
+
+ CTRACE((tfp, "dump_addrinfo %s:\n", tag));
+ for (res = (const LYNX_ADDRINFO *) data; res; res = res->ai_next) {
+ char hostbuf[1024], portbuf[1024];
+
+ ++count;
+ hostbuf[0] = '\0';
+ portbuf[0] = '\0';
+ getnameinfo(res->ai_addr, res->ai_addrlen,
+ hostbuf, (socklen_t) sizeof(hostbuf),
+ portbuf, (socklen_t) sizeof(portbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ CTRACE((tfp,
+ "\t[%d] family %d, socktype %d, protocol %d addr %s port %s\n",
+ count,
+ res->ai_family,
+ res->ai_socktype,
+ res->ai_protocol,
+ hostbuf,
+ portbuf));
+ }
+}
+
+#if defined(NSL_FORK)
+
+/*
+ * Copy the relevant information (on the child-side).
+ */
+static size_t fill_addrinfo(void **buffer,
+ const LYNX_ADDRINFO *phost)
+{
+ static const char *this_func = "fill_addinfo";
+
+ const LYNX_ADDRINFO *q;
+ LYNX_ADDRINFO *actual;
+ LYNX_ADDRINFO *result;
+ int count = 0;
+ int limit = 0;
+ size_t need = sizeof(LYNX_ADDRINFO);
+ char *heap;
+
+ CTRACE((tfp, "filladdr_info %p\n", (const void *) phost));
+ for (q = phost; q != 0; q = q->ai_next) {
+ ++limit;
+ need += phost->ai_addrlen;
+ need += sizeof(LYNX_ADDRINFO);
+ }
+ CTRACE((tfp, "...fill_addrinfo %d:%lu\n", limit, (unsigned long) need));
+
+ if ((result = (LYNX_ADDRINFO *) calloc(1, need)) == 0)
+ outofmem(__FILE__, this_func);
+
+ *buffer = actual = result;
+ heap = ((char *) actual) + ((size_t) limit * sizeof(LYNX_ADDRINFO));
+
+ for (count = 0; count < limit; ++count) {
+
+ /*
+ * copying the whole structure seems simpler but because it is not
+ * packed, uninitialized gaps make it hard to analyse with valgrind.
+ */
+ /* *INDENT-EQLS* */
+ actual->ai_flags = phost->ai_flags;
+ actual->ai_family = phost->ai_family;
+ actual->ai_socktype = phost->ai_socktype;
+ actual->ai_protocol = phost->ai_protocol;
+ actual->ai_addrlen = phost->ai_addrlen;
+ actual->ai_addr = (struct sockaddr *) (void *) heap;
+
+ MemCpy(heap, phost->ai_addr, phost->ai_addrlen);
+ heap += phost->ai_addrlen;
+
+ phost = phost->ai_next;
+
+ actual->ai_next = ((count + 1 < limit)
+ ? (actual + 1)
+ : 0);
+ ++actual;
+ }
+ return (size_t) (heap - (char *) result);
+}
+
+/*
+ * Read data, repair pointers as done in fill_addrinfo().
+ */
+static unsigned read_addrinfo(int fd, char *buffer, size_t length)
+{
+ unsigned result = read_bytes(fd, buffer, length);
+ LYNX_ADDRINFO *actual = (LYNX_ADDRINFO *) (void *) buffer;
+ LYNX_ADDRINFO *res;
+ int count = 0;
+ int limit;
+ char *heap;
+
+ CTRACE((tfp, "read_addrinfo length %lu\n", (unsigned long) length));
+ for (limit = 0; actual[limit].ai_next; ++limit) {
+ }
+ ++limit;
+ heap = (char *) (actual + limit);
+ CTRACE((tfp, "...read_addrinfo %d items\n", limit));
+
+ for (res = actual, count = 0; count < limit; ++count) {
+ res->ai_addr = (struct sockaddr *) (void *) heap;
+ heap += res->ai_addrlen;
+ if (count < limit - 1) {
+ res->ai_next = (res + 1);
+ ++res;
+ } else {
+ res->ai_next = 0;
+ }
+ }
+
+#ifdef DEBUG_HOSTENT
+ dump_addrinfo("read_addrinfo", buffer);
+#endif
+ return result;
+}
+
+/*
+ * This is called via the child-side of the fork.
+ */
+static void really_getaddrinfo(const char *host,
+ const char *port,
+ STATUSES * statuses,
+ void **result)
+{
+ LYNX_ADDRINFO hints, *res = 0;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(host, port, &hints, &res);
+ if (error) {
+ CTRACE((tfp, "HTGetAddrInfo: getaddrinfo(%s, %s): %s\n", host, port,
+ gai_strerror(error)));
+ } else {
+ statuses->child_errno = errno;
+ statuses->child_h_errno = h_errno;
+#ifdef HAVE_H_ERRNO
+ statuses->h_errno_valid = YES;
+#endif
+
+#ifdef DEBUG_HOSTENT_CHILD
+ dump_addrinfo("CHILD getaddrinfo", res);
+#endif
+ statuses->rehostentlen = fill_addrinfo(result, res);
+#ifdef DEBUG_HOSTENT_CHILD
+ dump_addrinfo("CHILD fill_addrinfo", (const LYNX_ADDRINFO *) (*result));
+#endif
+ if (statuses->rehostentlen <= sizeof(LYNX_ADDRINFO) || (*result) == NULL) {
+ statuses->rehostentlen = 0;
+ statuses->h_length = 0;
+ } else {
+ statuses->h_length = (int) (((LYNX_ADDRINFO *) (*result))->ai_addrlen);
+ }
+ freeaddrinfo(res);
+ }
+}
+#endif /* NSL_FORK */
+
+static LYNX_ADDRINFO *HTGetAddrInfo(const char *str,
+ const int defport)
+{
+#ifdef NSL_FORK
+ /* for transfer of result between from child to parent: */
+ void *readdrinfo = 0;
+
+#else
+ LYNX_ADDRINFO hints;
+ int error;
+#endif /* NSL_FORK */
+ LYNX_ADDRINFO *res;
+ 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;
+ }
+
+#ifdef NSL_FORK
+ if (setup_nsl_fork(really_getaddrinfo,
+ read_addrinfo,
+ dump_addrinfo,
+ host, port, &readdrinfo)) {
+ res = readdrinfo;
+ } else {
+ res = NULL;
+ }
+#else
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(host, port, &hints, &res);
+ if (error || !res) {
+ CTRACE((tfp, "HTGetAddrInfo: getaddrinfo(%s, %s): %s\n", host, port,
+ gai_strerror(error)));
+ res = NULL;
+ }
+#endif
+
+ free(s);
+#ifdef DEBUG_HOSTENT
+ dump_addrinfo("HTGetAddrInfo", res);
+#endif
+ return res;
+}
+
+BOOLEAN HTCheckAddrInfo(const char *str, const int defport)
+{
+ LYNX_ADDRINFO *data = HTGetAddrInfo(str, defport);
+ BOOLEAN result = (data != 0);
+
+ FREE_NSL_FORK(data);
+ return result;
+}
+#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)
+{
+ char *socks5_host = NULL;
+ unsigned socks5_host_len = 0;
+ int socks5_port;
+ const char *socks5_orig_url;
+ char *socks5_new_url = NULL;
+ char *socks5_protocol = NULL;
+ int status = HT_OK;
+ char *line = NULL;
+ char *p1 = NULL;
+ char *host = NULL;
+ char const *emsg;
+
+#ifdef INET6
+ LYNX_ADDRINFO *res = 0, *res0 = 0;
+
+#else
+ struct sockaddr_in sock_A;
+ struct sockaddr_in *soc_in = &sock_A;
+#endif
+
+ *s = -1; /* nothing is open yet */
+
+ /* In case of a present SOCKS5 proxy, marshal */
+ if (socks5_proxy == NULL)
+ socks5_proxy = LYGetEnv("SOCKS5_PROXY");
+ if ((socks5_orig_url = socks5_proxy) != NULL) {
+ int xport;
+
+ xport = default_port;
+ socks5_orig_url = url;
+ StrAllocCopy(socks5_new_url, url);
+
+ /* Get node name and optional port number of wanted URL */
+ if ((p1 = HTParse(socks5_new_url, "", PARSE_HOST)) != NULL) {
+ StrAllocCopy(socks5_host, p1);
+ strip_userid(socks5_host, FALSE);
+ FREE(p1);
+ }
+
+ if (isEmpty(socks5_host)) {
+ emsg = gettext("SOCKS5: no hostname found.");
+ status = HT_ERROR;
+ goto report_error;
+ }
+
+ if (strlen(socks5_host) > 255) {
+ emsg = gettext("SOCKS5: hostname too long.");
+ status = HT_ERROR;
+ goto report_error;
+ }
+ socks5_host_len = (unsigned) strlen(socks5_host);
+
+ if (HTParsePort(socks5_new_url, &socks5_port) == NULL)
+ socks5_port = xport;
+ FREE(socks5_new_url);
+
+ /* And switch over to our SOCKS5 config; in order to embed that into
+ * lynx environment, prepend protocol prefix */
+ default_port = 1080; /* RFC 1928 */
+ HTSACat(&socks5_new_url, "socks://");
+ HTSACat(&socks5_new_url, socks5_proxy);
+ url = socks5_new_url;
+
+ HTSprintf0(&socks5_protocol,
+ gettext("(for %s at %s) SOCKS5"),
+ protocol, socks5_host);
+ protocol = socks5_protocol;
+ }
+#ifndef INET6
+ /*
+ * 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);
+ StrAllocCopy(host, p1);
+ strip_userid(host, FALSE);
+ 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);
+ status = HT_NO_DATA;
+ goto cleanup;
+ }
+#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;
+ }
+ goto cleanup;
+ }
+#endif /* INET6 */
+
+ HTSprintf0(&line, gettext("Making %s connection to %s"), protocol, host);
+ _HTProgress(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) {
+ status = HT_NO_DATA;
+ emsg = gettext("socket failed.");
+ goto report_error;
+ }
+#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);
+ 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, SOCKADDR_OF(sock_A), sizeof(sock_A));
+#endif /* INET6 */
+ } else
+#endif /* SOCKS */
+#ifdef INET6
+ status = connect(*s, res->ai_addr, res->ai_addrlen);
+#else
+ status = connect(*s, SOCKADDR_OF(sock_A), sizeof(sock_A));
+#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.
+ */
+ CTRACE((tfp, "connect(): status: %d, SOCK_ERRNO: %d\n", status, SOCKET_ERRNO));
+
+ 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
+ if (SOCKET_ERRNO != EINPROGRESS) {
+ 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
+#ifndef NSL_FORK
+ if (res0)
+ freeaddrinfo(res0);
+#endif
+#endif /* INET6 */
+ status = HT_NO_DATA;
+ goto cleanup;
+ }
+ set_timeout(&select_timeout);
+ FD_ZERO(&writefds);
+ FD_SET((LYNX_FD) *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) {
+ if (SOCKET_ERRNO != EINPROGRESS) {
+ 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) && (SOCKET_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 (((SOCKET_ERRNO == EALREADY) ||
+ (SOCKET_ERRNO == EINPROGRESS)) &&
+ HTCheckForInterrupt()) {
+ status = HT_INTERRUPTED;
+ 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, SOCKADDR_OF(sock_A), sizeof(sock_A));
+#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, SOCKADDR_OF(sock_A), sizeof(sock_A));
+#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;
+ if (status != HT_INTERRUPTED)
+ 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
+#ifdef NSL_FORK
+ FREE_NSL_FORK(res0);
+#else
+ if (res0)
+ freeaddrinfo(res0);
+#endif
+#endif /* INET6 */
+
+ /* Now if this was a SOCKS5 proxy connection, go for the real one */
+ if (status >= 0 && socks5_orig_url != NULL) {
+ unsigned char pbuf[4 + 1 + 255 + 2];
+ unsigned i;
+
+ /* RFC 1928: version identifier/method selection message */
+ pbuf[0] = 0x05; /* VER: protocol version: X'05' */
+ pbuf[1] = 0x01; /* NMETHODS: 1 */
+ pbuf[2] = 0x00; /* METHOD: X'00' NO AUTHENTICATION REQUIRED */
+ if (NETWRITE(*s, (char *) pbuf, 3) != 3) {
+ goto report_system_err;
+ } else if (HTDoRead(*s, pbuf, 2) != 2) {
+ goto report_system_err;
+ } else if (pbuf[0] != 0x05 || pbuf[1] != 0x00) {
+ goto report_unexpected_reply;
+ }
+
+ /* RFC 1928: CONNECT request */
+ HTSprintf0(&line, gettext("SOCKS5: connecting to %s"), socks5_host);
+ _HTProgress(line);
+ pbuf[0] = 0x05; /* VER: protocol version: X'05' */
+ pbuf[1] = 0x01; /* CMD: CONNECT X'01' */
+ pbuf[2] = 0x00; /* RESERVED */
+ pbuf[3] = 0x03; /* ATYP: domain name */
+ pbuf[4] = (unsigned char) socks5_host_len;
+ memcpy(&pbuf[i = 5], socks5_host, socks5_host_len);
+ i += socks5_host_len;
+ /* C99 */ {
+ unsigned short x; /* XXX 16-bit? */
+
+ x = htons((PortNumber) socks5_port);
+ memcpy(&pbuf[i], (unsigned char *) &x, sizeof x);
+ i += (unsigned) sizeof(x);
+ }
+ if ((size_t) NETWRITE(*s, (char *) pbuf, i) != i) {
+ goto report_system_err;
+ } else if ((unsigned) HTDoRead(*s, pbuf, 4) != 4) {
+ goto report_system_err;
+ }
+ /* Version 5, reserved must be 0 */
+ if (pbuf[0] == 0x05 && pbuf[2] == 0x00) {
+ /* Result */
+ switch (pbuf[1]) {
+ case 0x00:
+ emsg = NULL;
+ break;
+ case 0x01:
+ emsg = gettext("SOCKS server failure");
+ break;
+ case 0x02:
+ emsg = gettext("connection not allowed by ruleset");
+ break;
+ case 0x03:
+ emsg = gettext("network unreachable");
+ break;
+ case 0x04:
+ emsg = gettext("host unreachable");
+ break;
+ case 0x05:
+ emsg = gettext("connection refused");
+ break;
+ case 0x06:
+ emsg = gettext("TTL expired");
+ break;
+ case 0x07:
+ emsg = gettext("command not supported");
+ break;
+ case 0x08:
+ emsg = gettext("address type not supported");
+ break;
+ default:
+ emsg = gettext("unknown SOCKS error code");
+ break;
+ }
+ if (emsg != NULL) {
+ goto report_no_connection;
+ }
+ } else {
+ goto report_unexpected_reply;
+ }
+
+ /* Address type variable; read the BND.PORT with it.
+ * This is actually false since RFC 1928 says that the BND.ADDR reply
+ * to CONNECT contains the IP address, so only 0x01 and 0x04 are
+ * allowed */
+ switch (pbuf[3]) {
+ case 0x01:
+ i = 4;
+ break;
+ case 0x03:
+ i = 1;
+ break;
+ case 0x04:
+ i = 16;
+ break;
+ default:
+ goto report_unexpected_reply;
+ }
+ i += (unsigned) sizeof(unsigned short);
+
+ if ((size_t) HTDoRead(*s, pbuf, i) != i) {
+ goto report_system_err;
+ } else if (i == 1 + sizeof(unsigned short)) {
+ i = pbuf[0];
+ if ((size_t) HTDoRead(*s, pbuf, i) != i) {
+ goto report_system_err;
+ }
+ }
+ }
+ goto cleanup;
+
+ report_system_err:
+ emsg = LYStrerror(errno);
+ goto report_no_connection;
+
+ report_unexpected_reply:
+ emsg = gettext("unexpected reply\n");
+ /* FALLTHRU */
+
+ report_no_connection:
+ status = HT_NO_CONNECTION;
+ /* FALLTHRU */
+
+ report_error:
+ HTAlert(emsg);
+ if (*s != -1) {
+ NETCLOSE(*s);
+ }
+
+ cleanup:
+ if (socks5_proxy != NULL) {
+ FREE(socks5_new_url);
+ FREE(socks5_protocol);
+ FREE(socks5_host);
+ }
+ FREE(host);
+ FREE(line);
+ return status;
+}
+
+/*
+ * This is interruptible so 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((LYNX_FD) 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 = NETREAD(fildes, (char *) 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 */