/*
* $LynxId: HTTCP.c,v 1.152 2019/09/19 00:27:19 Steffen.Nurpmeso 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