1 /*
2  * $LynxId: HTTCP.c,v 1.126 2013/07/21 00:22:52 tom Exp $
3  *
4  *			Generic Communication Code		HTTCP.c
5  *			==========================
6  *
7  *	This code is in common between client and server sides.
8  *
9  *	16 Jan 92  TBL	Fix strtol() undefined on CMU Mach.
10  *	25 Jun 92  JFG	Added DECNET option through TCP socket emulation.
11  *	13 Sep 93  MD	Added correct return of vmserrorno for HTInetStatus.
12  *			Added decoding of vms error message for MULTINET.
13  *	7-DEC-1993 Bjorn S. Nilsson, ALEPH, CERN, VMS UCX ioctl() changes
14  *			(done of Mosaic)
15  *	19 Feb 94  Danny Mayer	Added Bjorn Fixes to Lynx version
16  *	 7 Mar 94  Danny Mayer	Added Fix UCX version for full domain name
17  *	20 May 94  Andy Harper	Added support for CMU TCP/IP transport
18  *	17 Nov 94  Andy Harper	Added support for SOCKETSHR transport
19  *	16 Jul 95  S. Bjorndahl added kluge to deal with LIBCMU bug
20  */
21 
22 #include <HTUtils.h>
23 #include <HTParse.h>
24 #include <HTAlert.h>
25 #include <HTTCP.h>
26 #include <LYGlobalDefs.h>	/* added for no_suspend */
27 #include <LYUtils.h>
28 
29 #ifdef NSL_FORK
30 #include <signal.h>
31 #include <www_wait.h>
32 #endif /* NSL_FORK */
33 
34 #ifdef HAVE_RESOLV_H
35 #include <resolv.h>
36 #endif
37 
38 #ifdef __DJGPP__
39 #include <netdb.h>
40 #endif /* __DJGPP__ */
41 
42 #define LYNX_ADDRINFO	struct addrinfo
43 #define LYNX_HOSTENT	struct hostent
44 
45 #define OK_HOST(p) ((p) != 0 && ((p)->h_length) != 0)
46 
47 #ifdef SVR4_BSDSELECT
48 int BSDselect(int nfds,
49 	      fd_set * readfds,
50 	      fd_set * writefds,
51 	      fd_set * exceptfds,
52 	      struct timeval *select_timeout);
53 
54 #ifdef select
55 #undef select
56 #endif /* select */
57 #define select BSDselect
58 #ifdef SOCKS
59 #ifdef Rselect
60 #undef Rselect
61 #endif /* Rselect */
62 #define Rselect BSDselect
63 #endif /* SOCKS */
64 #endif /* SVR4_BSDSELECT */
65 
66 #include <LYLeaks.h>
67 
68 /*
69  *  Module-Wide variables
70  */
71 static char *hostname = NULL;	/* The name of this host */
72 
73 /*
74  *  PUBLIC VARIABLES
75  */
76 #ifdef SOCKS
77 unsigned long socks_bind_remoteAddr;	/* for long Rbind */
78 #endif /* SOCKS */
79 
80 /*	Encode INET status (as in sys/errno.h)			  inet_status()
81  *	------------------
82  *
83  *  On entry,
84  *	where		gives a description of what caused the error
85  *	global errno	gives the error number in the Unix way.
86  *
87  *  On return,
88  *	returns		a negative status in the Unix way.
89  */
90 
91 #ifdef DECL_SYS_ERRLIST
92 extern char *sys_errlist[];	/* see man perror on cernvax */
93 extern int sys_nerr;
94 #endif /* DECL_SYS_ERRLIST */
95 
96 #ifdef __DJGPP__
ResolveYield(void)97 static int ResolveYield(void)
98 {
99     return HTCheckForInterrupt()? 0 : 1;
100 }
101 #endif
102 
103 #if defined(VMS) && defined(UCX)
104 /*
105  *  A routine to mimic the ioctl function for UCX.
106  *  Bjorn S. Nilsson, 25-Nov-1993. Based on an example in the UCX manual.
107  */
108 #include <HTioctl.h>
109 
HTioctl(int d,int request,int * argp)110 int HTioctl(int d,
111 	    int request,
112 	    int *argp)
113 {
114     int sdc, status;
115     unsigned short fun, iosb[4];
116     char *p5, *p6;
117     struct comm {
118 	int command;
119 	char *addr;
120     } ioctl_comm;
121     struct it2 {
122 	unsigned short len;
123 	unsigned short opt;
124 	struct comm *addr;
125     } ioctl_desc;
126 
127     if ((sdc = vaxc$get_sdc(d)) == 0) {
128 	set_errno(EBADF);
129 	return -1;
130     }
131     ioctl_desc.opt = UCX$C_IOCTL;
132     ioctl_desc.len = sizeof(struct comm);
133 
134     ioctl_desc.addr = &ioctl_comm;
135     if (request & IOC_OUT) {
136 	fun = IO$_SENSEMODE;
137 	p5 = 0;
138 	p6 = (char *) &ioctl_desc;
139     } else {
140 	fun = IO$_SETMODE;
141 	p5 = (char *) &ioctl_desc;
142 	p6 = 0;
143     }
144     ioctl_comm.command = request;
145     ioctl_comm.addr = (char *) argp;
146     status = sys$qiow(0, sdc, fun, iosb, 0, 0, 0, 0, 0, 0, p5, p6);
147     if (!(status & 01)) {
148 	set_errno(status);
149 	return -1;
150     }
151     if (!(iosb[0] & 01)) {
152 	set_errno(iosb[0]);
153 	return -1;
154     }
155     return 0;
156 }
157 #endif /* VMS && UCX */
158 
159 #define MY_FORMAT "TCP: Error %d in `SOCKET_ERRNO' after call to %s() failed.\n\t%s\n"
160 	   /* third arg is transport/platform specific */
161 
162 /*	Report Internet Error
163  *	---------------------
164  */
HTInetStatus(const char * where)165 int HTInetStatus(const char *where)
166 {
167     int status;
168     int saved_errno = errno;
169 
170 #ifdef VMS
171 #ifdef MULTINET
172     SOCKET_ERRNO = vmserrno;
173 #endif /* MULTINET */
174 #endif /* VMS */
175 
176 #ifdef VM
177     CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
178 	    "(Error number not translated)"));	/* What Is the VM equiv? */
179 #define ER_NO_TRANS_DONE
180 #endif /* VM */
181 
182 #ifdef VMS
183 #ifdef MULTINET
184     CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
185 	    vms_errno_string()));
186 #else
187     CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
188 	    ((SOCKET_ERRNO > 0 && SOCKET_ERRNO <= 65) ?
189 	     strerror(SOCKET_ERRNO) : "(Error number not translated)")));
190 #endif /* MULTINET */
191 #define ER_NO_TRANS_DONE
192 #endif /* VMS */
193 
194 #ifdef HAVE_STRERROR
195     CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
196 	    strerror(SOCKET_ERRNO)));
197 #define ER_NO_TRANS_DONE
198 #endif /* HAVE_STRERROR */
199 
200 #ifndef ER_NO_TRANS_DONE
201     CTRACE((tfp, MY_FORMAT, SOCKET_ERRNO, where,
202 	    (SOCKET_ERRNO < sys_nerr ?
203 	     sys_errlist[SOCKET_ERRNO] : "Unknown error")));
204 #endif /* !ER_NO_TRANS_DONE */
205 
206 #ifdef VMS
207 #ifndef MULTINET
208     CTRACE((tfp,
209 	    "         Unix error number (SOCKET_ERRNO) = %ld dec\n",
210 	    SOCKET_ERRNO));
211     CTRACE((tfp,
212 	    "         VMS error (vaxc$errno)    = %lx hex\n",
213 	    vaxc$errno));
214 #endif /* MULTINET */
215 #endif /* VMS */
216 
217     set_errno(saved_errno);
218 
219 #ifdef VMS
220     /*
221      * uerrno and errno happen to be zero if vmserrno <> 0
222      */
223 #ifdef MULTINET
224     status = -vmserrno;
225 #else
226     status = -vaxc$errno;
227 #endif /* MULTINET */
228 #else
229     status = -SOCKET_ERRNO;
230 #endif /* VMS */
231     return status;
232 }
233 
234 /*	Parse a cardinal value				       parse_cardinal()
235  *	----------------------
236  *
237  * On entry,
238  *	*pp	    points to first character to be interpreted, terminated by
239  *		    non 0:9 character.
240  *	*pstatus    points to status already valid
241  *	maxvalue    gives the largest allowable value.
242  *
243  * On exit,
244  *	*pp	    points to first unread character
245  *	*pstatus    points to status updated iff bad
246  */
HTCardinal(int * pstatus,char ** pp,unsigned int max_value)247 unsigned int HTCardinal(int *pstatus,
248 			char **pp,
249 			unsigned int max_value)
250 {
251     unsigned int n;
252 
253     if ((**pp < '0') || (**pp > '9')) {		/* Null string is error */
254 	*pstatus = -3;		/* No number where one expected */
255 	return 0;
256     }
257 
258     n = 0;
259     while ((**pp >= '0') && (**pp <= '9'))
260 	n = n * 10 + (unsigned) (*((*pp)++) - '0');
261 
262     if (n > max_value) {
263 	*pstatus = -4;		/* Cardinal outside range */
264 	return 0;
265     }
266 
267     return n;
268 }
269 
270 #ifndef DECNET			/* Function only used below for a trace message */
271 /*	Produce a string for an Internet address
272  *	----------------------------------------
273  *
274  *  On exit,
275  *	returns a pointer to a static string which must be copied if
276  *		it is to be kept.
277  */
HTInetString(SockA * soc_in)278 const char *HTInetString(SockA * soc_in)
279 {
280 #ifdef INET6
281     static char hostbuf[MAXHOSTNAMELEN];
282 
283     getnameinfo((struct sockaddr *) soc_in,
284 		SOCKADDR_LEN(soc_in),
285 		hostbuf, (socklen_t) sizeof(hostbuf),
286 		NULL, 0,
287 		NI_NUMERICHOST);
288     return hostbuf;
289 #else
290     static char string[20];
291 
292     sprintf(string, "%d.%d.%d.%d",
293 	    (int) *((unsigned char *) (&soc_in->sin_addr) + 0),
294 	    (int) *((unsigned char *) (&soc_in->sin_addr) + 1),
295 	    (int) *((unsigned char *) (&soc_in->sin_addr) + 2),
296 	    (int) *((unsigned char *) (&soc_in->sin_addr) + 3));
297     return string;
298 #endif /* INET6 */
299 }
300 #endif /* !DECNET */
301 
302 /*	Check whether string is a valid Internet hostname - kw
303  *	-------------------------------------------------
304  *
305  *  Checks whether
306  *  - contains only valid chars for domain names (actually, the
307  *    restrictions are somewhat relaxed),
308  *  - no leading dots or empty segments,
309  *  - no segment starts with '-' or '+' [this protects telnet command],
310  *  - max. length of dot-separated segment <= 63 (RFC 1034,1035),
311  *  - total length <= 254 (if it ends with dot) or 253 (otherwise)
312  *     [an interpretation of RFC 1034,1035, although RFC 1123
313  *      suggests 255 as limit - kw].
314  *
315  *  Note: user (before '@') and port (after ':') components from
316  *      host part of URL should be already stripped (if appropriate)
317  *      from the input string.
318  *
319  *  On exit,
320  *	returns 1 if valid, otherwise 0.
321  */
valid_hostname(char * name)322 BOOL valid_hostname(char *name)
323 {
324     int i = 1, iseg = 0;
325     char *cp = name;
326 
327     if (!(name && *name))
328 	return NO;
329     for (; (*cp && i <= 253); cp++, i++) {
330 	if (*cp == '.') {
331 	    if (iseg == 0) {
332 		return NO;
333 	    } else {
334 		iseg = 0;
335 		continue;
336 	    }
337 	} else if (iseg == 0 && (*cp == '-' || *cp == '+')) {
338 	    return NO;
339 	} else if (++iseg > 63) {
340 	    return NO;
341 	}
342 	if (!isalnum(UCH(*cp)) &&
343 	    *cp != '-' && *cp != '_' &&
344 	    *cp != '$' && *cp != '+') {
345 	    return NO;
346 	}
347     }
348     return (BOOL) (*cp == '\0' || (*cp == '.' && iseg != 0 && cp[1] == '\0'));
349 }
350 
351 /* for transfer of status from child to parent: */
352 typedef struct _statuses {
353     size_t rehostentlen;
354     int h_length;
355     int child_errno;		/* sometimes useful to pass this on */
356     int child_h_errno;
357     BOOL h_errno_valid;
358 } STATUSES;
359 
360 /*
361  *  Function to allow us to be killed with a normal signal (not
362  *  SIGKILL), but don't go through normal libc exit() processing, which
363  *  would screw up parent's stdio.  -BL
364  */
365 #ifdef NSL_FORK
quench(int sig GCC_UNUSED)366 static void quench(int sig GCC_UNUSED)
367 {
368     _exit(2);
369 }
370 #endif
371 
372 int lynx_nsl_status = HT_OK;
373 
374 #define DEBUG_HOSTENT		/* disable in case of problems */
375 #define DEBUG_HOSTENT_CHILD	/* for NSL_FORK, may screw up trace file */
376 
377 /*
378  *  dump_hostent - dumps the contents of a LYNX_HOSTENT to the
379  *  trace log or stderr, including all pointer values, strings, and
380  *  addresses, in a format inspired by gdb's print format. - kw
381  */
dump_hostent(const char * msgprefix,void * data)382 static void dump_hostent(const char *msgprefix,
383 			 void *data)
384 {
385     if (TRACE) {
386 	int i;
387 	char **pcnt;
388 	const LYNX_HOSTENT *phost = data;
389 
390 	CTRACE((tfp, "%s: %p ", msgprefix, (const void *) phost));
391 	if (phost) {
392 	    CTRACE((tfp, "{ h_name = %p", phost->h_name));
393 	    if (phost->h_name) {
394 		CTRACE((tfp, " \"%s\",", phost->h_name));
395 	    } else {
396 		CTRACE((tfp, ","));
397 	    }
398 	    CTRACE((tfp, "\n\t h_aliases = %p", (void *) phost->h_aliases));
399 	    if (phost->h_aliases) {
400 		CTRACE((tfp, " {"));
401 		for (pcnt = phost->h_aliases; *pcnt; pcnt++) {
402 		    CTRACE((tfp, "%s %p \"%s\"",
403 			    (pcnt == phost->h_aliases ? " " : ", "),
404 			    *pcnt, *pcnt));
405 		}
406 		CTRACE((tfp, "%s0x0 },\n\t",
407 			(*phost->h_aliases ? ", " : " ")));
408 	    } else {
409 		CTRACE((tfp, ",\n\t"));
410 	    }
411 	    CTRACE((tfp, " h_addrtype = %d,", phost->h_addrtype));
412 	    CTRACE((tfp, " h_length = %d,\n\t", phost->h_length));
413 	    CTRACE((tfp, " h_addr_list = %p", (void *) phost->h_addr_list));
414 	    if (phost->h_addr_list) {
415 		CTRACE((tfp, " {"));
416 		for (pcnt = phost->h_addr_list; *pcnt; pcnt++) {
417 		    CTRACE((tfp, "%s %p",
418 			    (pcnt == phost->h_addr_list ? "" : ","),
419 			    *pcnt));
420 		    for (i = 0; i < phost->h_length; i++) {
421 			CTRACE((tfp, "%s%d%s", (i == 0 ? " \"" : "."),
422 				(int) *((unsigned char *) (*pcnt) + i),
423 				(i + 1 == phost->h_length ? "\"" : "")));
424 		    }
425 		}
426 		if (*phost->h_addr_list) {
427 		    CTRACE((tfp, ", 0x0 } }"));
428 		} else {
429 		    CTRACE((tfp, " 0x0 } }"));
430 		}
431 	    } else {
432 		CTRACE((tfp, "}"));
433 	    }
434 	}
435 	CTRACE((tfp, "\n"));
436 	fflush(tfp);
437     }
438 }
439 
440 #ifdef NSL_FORK
441 
442 /*
443  * Even though it is a small amount, we cannot count on reading the whole
444  * struct via a pipe in one read -TD
445  */
read_bytes(int fd,char * buffer,size_t length)446 static unsigned read_bytes(int fd, char *buffer, size_t length)
447 {
448     unsigned result = 0;
449 
450     while (length != 0) {
451 	unsigned got = (unsigned) read(fd, buffer, length);
452 
453 	if ((int) got > 0) {
454 	    result += got;
455 	    buffer += got;
456 	    length -= got;
457 	} else {
458 	    break;
459 	}
460     }
461     return result;
462 }
463 
read_hostent(int fd,char * buffer,size_t length)464 static unsigned read_hostent(int fd, char *buffer, size_t length)
465 {
466     unsigned have = read_bytes(fd, buffer, length);
467 
468     if (have) {
469 	LYNX_HOSTENT *data = (LYNX_HOSTENT *) (void *) buffer;
470 	char *next_char = (char *) data + sizeof(*data);
471 	char **next_ptr = (char **) (void *) next_char;
472 	long offset = 0;
473 	int n;
474 	int num_addrs = 0;
475 	int num_aliases = 0;
476 
477 	if (data->h_addr_list) {
478 	    data->h_addr_list = next_ptr;
479 	    while (next_ptr[num_addrs] != 0) {
480 		++num_addrs;
481 	    }
482 	    next_ptr += (num_addrs + 1);
483 	    next_char += (size_t) (num_addrs + 1) * sizeof(data->h_addr_list[0]);
484 	}
485 
486 	if (data->h_aliases) {
487 	    data->h_aliases = next_ptr;
488 	    while (next_ptr[num_aliases] != 0) {
489 		++num_aliases;
490 	    }
491 	    next_char += (size_t) (num_aliases + 1) * sizeof(data->h_aliases[0]);
492 	}
493 
494 	if (data->h_name) {
495 	    offset = next_char - data->h_name;
496 	    data->h_name = next_char;
497 	} else if (data->h_addr_list) {
498 	    offset = next_char - (char *) data->h_addr_list[0];
499 	} else if (data->h_aliases) {
500 	    offset = next_char - (char *) data->h_aliases[0];
501 	}
502 
503 	if (data->h_addr_list) {
504 	    for (n = 0; n < num_addrs; ++n) {
505 		data->h_addr_list[n] += offset;
506 	    }
507 	}
508 
509 	if (data->h_aliases) {
510 	    for (n = 0; n < num_aliases; ++n) {
511 		data->h_aliases[n] += offset;
512 	    }
513 	}
514     }
515 
516     return have;
517 }
518 #endif /* NSL_FORK */
519 
520 /*
521  *  fill_rehostent - copies as much as possible relevant content from
522  *  the LYNX_HOSTENT pointed to by phost to the char buffer given
523  *  by rehostent, subject to maximum output length rehostentsize,
524  *  following pointers and building self-contained output which can be
525  *  cast to a LYNX_HOSTENT. - kw
526  *  See also description of LYGetHostByName.
527  */
528 #if defined(NSL_FORK) || defined(_WINDOWS_NSL)
529 
530 #define REHOSTENT_SIZE 128	/* not bigger than pipe buffer! */
531 
532 typedef struct {
533     LYNX_HOSTENT h;
534     char rest[REHOSTENT_SIZE];
535 } AlignedHOSTENT;
536 
fill_rehostent(void ** rehostent,const LYNX_HOSTENT * phost)537 static size_t fill_rehostent(void **rehostent,
538 			     const LYNX_HOSTENT *phost)
539 {
540     LYNX_HOSTENT *data = 0;
541     int num_addrs = 0;
542     int num_aliases = 0;
543     char *result = 0;
544     char *p_next_char;
545     char **p_next_charptr;
546     size_t name_len = 0;
547     size_t need = sizeof(LYNX_HOSTENT);
548     int n;
549 
550     if (!phost)
551 	return 0;
552 
553     if (phost->h_name) {
554 	name_len = strlen(phost->h_name);
555 	need += name_len + 1;
556     }
557     if (phost->h_addr_list) {
558 	while (phost->h_addr_list[num_addrs]) {
559 	    num_addrs++;
560 	}
561 	need += ((size_t) num_addrs + 1) * ((size_t) phost->h_length
562 					    + sizeof(phost->h_addr_list[0]));
563     }
564     if (phost->h_aliases) {
565 	while (phost->h_aliases[num_aliases]) {
566 	    need += strlen(phost->h_aliases[num_aliases]) + 1;
567 	    num_aliases++;
568 	}
569 	need += ((size_t) num_aliases + 1) * sizeof(phost->h_aliases[0]);
570     }
571 
572     if ((result = calloc(need, sizeof(char))) == 0)
573 	  outofmem(__FILE__, "fill_rehostent");
574 
575     *rehostent = result;
576 
577     data = (LYNX_HOSTENT *) (void *) result;
578 
579     data->h_addrtype = phost->h_addrtype;
580     data->h_length = phost->h_length;
581 
582     p_next_char = result + sizeof(LYNX_HOSTENT);
583 
584     p_next_charptr = (char **) (void *) p_next_char;
585     if (phost->h_addr_list)
586 	p_next_char += (size_t) (num_addrs + 1) * sizeof(phost->h_addr_list[0]);
587     if (phost->h_aliases)
588 	p_next_char += (size_t) (num_aliases + 1) * sizeof(phost->h_aliases[0]);
589 
590     if (phost->h_name) {
591 	data->h_name = p_next_char;
592 	strcpy(p_next_char, phost->h_name);
593 	p_next_char += name_len + 1;
594     }
595 
596     if (phost->h_addr_list) {
597 	data->h_addr_list = p_next_charptr;
598 	for (n = 0; n < num_addrs; ++n) {
599 	    MemCpy(p_next_char, phost->h_addr_list[n], phost->h_length);
600 	    *p_next_charptr++ = p_next_char;
601 	    p_next_char += phost->h_length;
602 	}
603 	++p_next_charptr;
604     }
605 
606     if (phost->h_aliases) {
607 	data->h_aliases = p_next_charptr;
608 	for (n = 0; n < num_aliases; ++n) {
609 	    strcpy(p_next_char, phost->h_aliases[n]);
610 	    *p_next_charptr++ = p_next_char;
611 	    p_next_char += strlen(phost->h_aliases[n]) + 1;;
612 	}
613     }
614     return need;
615 }
616 #endif /* NSL_FORK */
617 
618 /*
619  * This chunk of code is used in both win32 and cygwin.
620  */
621 #if defined(_WINDOWS_NSL)
622 static LYNX_HOSTENT *gbl_phost;	/* Pointer to host - See netdb.h */
623 
624 #if !(defined(__CYGWIN__) && defined(NSL_FORK))
625 static int donelookup;
626 
_fork_func(void * arg)627 static unsigned long __stdcall _fork_func(void *arg)
628 {
629     const char *host = (const char *) arg;
630     static AlignedHOSTENT aligned_full_rehostent;
631     char *rehostent = (char *) &aligned_full_rehostent;
632     size_t rehostentlen = 0;
633 
634 #ifdef SH_EX
635     unsigned long addr;
636 
637     addr = (unsigned long) inet_addr(host);
638     if (addr != INADDR_NONE)
639 	gbl_phost = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
640     else
641 	gbl_phost = gethostbyname(host);
642 #else
643     gbl_phost = gethostbyname(host);
644 #endif
645 
646     if (gbl_phost) {
647 	rehostentlen = fill_rehostent((void **) &rehostent, gbl_phost);
648 	if (rehostentlen == 0) {
649 	    gbl_phost = (LYNX_HOSTENT *) NULL;
650 	} else {
651 	    gbl_phost = (LYNX_HOSTENT *) rehostent;
652 	}
653     }
654 
655     donelookup = TRUE;
656     return (unsigned long) (gbl_phost);
657 }
658 #endif /* __CYGWIN__ */
659 #endif /* _WINDOWS_NSL */
660 
661 #ifdef NSL_FORK
662 
663 #ifndef HAVE_H_ERRNO
664 #undef  h_errno
665 #define h_errno my_errno
666 static int my_errno;
667 
668 #else /* we do HAVE_H_ERRNO: */
669 #ifndef h_errno			/* there may be a macro as well as the extern data */
670 extern int h_errno;
671 #endif
672 #endif
673 
setup_nsl_fork(void (* really)(char *,char *,STATUSES *,void **),unsigned (* readit)(int,char *,size_t),void (* dumpit)(const char *,void *),char * host,char * port,void ** rehostent)674 static BOOL setup_nsl_fork(void (*really) (char *, char *, STATUSES *, void **),
675 			   unsigned (*readit) (int, char *, size_t),
676 			   void (*dumpit) (const char *, void *),
677 			   char *host,
678 			   char *port,
679 			   void **rehostent)
680 {
681     STATUSES statuses;
682 
683     /*
684      * fork-based gethostbyname() with checks for interrupts.
685      * - Tom Zerucha (tz@execpc.com) & FM
686      */
687     int got_rehostent = 0;
688 
689 #if HAVE_SIGACTION
690     sigset_t old_sigset;
691     sigset_t new_sigset;
692 #endif
693     /*
694      * Pipe, child pid, status buffers, start time, select() control
695      * variables.
696      */
697     int fpid, waitret;
698     int pfd[2], selret;
699     unsigned readret;
700 
701 #ifdef HAVE_TYPE_UNIONWAIT
702     union wait waitstat;
703 
704 #else
705     int waitstat = 0;
706 #endif
707     time_t start_time = time((time_t *) 0);
708     fd_set readfds;
709     struct timeval one_second;
710     long dns_patience = 30;	/* how many seconds will we wait for DNS? */
711     int child_exited = 0;
712 
713     memset(&statuses, 0, sizeof(STATUSES));
714     statuses.h_errno_valid = NO;
715 
716     /*
717      * Reap any children that have terminated since last time through.
718      * This might include children that we killed, then waited with WNOHANG
719      * before they were actually ready to be reaped.  (Should be max of 1
720      * in this state, but the loop is safe if waitpid() is implemented
721      * correctly:  returns 0 when children exist but none have exited; -1
722      * with errno == ECHILD when no children.) -BL
723      */
724     do {
725 	waitret = waitpid(-1, 0, WNOHANG);
726     } while (waitret > 0 || (waitret == -1 && errno == EINTR));
727     waitret = 0;
728 
729     IGNORE_RC(pipe(pfd));
730 
731 #if HAVE_SIGACTION
732     /*
733      * Attempt to prevent a rare situation where the child could execute
734      * the Lynx signal handlers because it gets killed before it even has a
735      * chance to reset its handlers, resulting in bogus 'Exiting via
736      * interrupt' message and screen corruption or worse.
737      * Should that continue to be reported, for systems without
738      * sigprocmask(), we need to find a different solutions for those.  -
739      * kw 19990430
740      */
741     sigemptyset(&new_sigset);
742     sigaddset(&new_sigset, SIGTERM);
743     sigaddset(&new_sigset, SIGINT);
744 #ifndef NOSIGHUP
745     sigaddset(&new_sigset, SIGHUP);
746 #endif /* NOSIGHUP */
747 #ifdef SIGTSTP
748     sigaddset(&new_sigset, SIGTSTP);
749 #endif /* SIGTSTP */
750 #ifdef SIGWINCH
751     sigaddset(&new_sigset, SIGWINCH);
752 #endif /* SIGWINCH */
753     sigprocmask(SIG_BLOCK, &new_sigset, &old_sigset);
754 #endif /* HAVE_SIGACTION */
755 
756     if ((fpid = fork()) == 0) {
757 	/*
758 	 * Child - for the long call.
759 	 *
760 	 * Make sure parent can kill us at will.  -BL
761 	 */
762 	(void) signal(SIGTERM, quench);
763 
764 	/*
765 	 * Also make sure the child does not run one of the signal handlers
766 	 * that may have been installed by Lynx if one of those signals
767 	 * occurs.  For example we don't want the child to remove temp
768 	 * files on ^C, let the parent deal with that.  - kw
769 	 */
770 	(void) signal(SIGINT, quench);
771 #ifndef NOSIGHUP
772 	(void) signal(SIGHUP, quench);
773 #endif /* NOSIGHUP */
774 #ifdef SIGTSTP
775 	if (no_suspend)
776 	    (void) signal(SIGTSTP, SIG_IGN);
777 	else
778 	    (void) signal(SIGTSTP, SIG_DFL);
779 #endif /* SIGTSTP */
780 #ifdef SIGWINCH
781 	(void) signal(SIGWINCH, SIG_IGN);
782 #endif /* SIGWINCH */
783 #ifndef __linux__
784 #ifndef DOSPATH
785 	signal(SIGBUS, SIG_DFL);
786 #endif /* DOSPATH */
787 #endif /* !__linux__ */
788 	signal(SIGSEGV, SIG_DFL);
789 	signal(SIGILL, SIG_DFL);
790 
791 #if HAVE_SIGACTION
792 	/* Restore signal mask to whatever it was before the fork. -kw */
793 	sigprocmask(SIG_SETMASK, &old_sigset, NULL);
794 #endif /* HAVE_SIGACTION */
795 
796 	/*
797 	 * Child won't use read side.  -BL
798 	 */
799 	close(pfd[0]);
800 #ifdef HAVE_H_ERRNO
801 	/* to detect cases when it doesn't get set although it should */
802 	h_errno = -2;
803 #endif
804 	set_errno(0);
805 	really(host, port, &statuses, rehostent);
806 	/*
807 	 * Send variables indicating status of lookup to parent.  That
808 	 * includes rehostentlen, which the parent will use as the size for
809 	 * the second read (if > 0).
810 	 */
811 	if (!statuses.child_errno)
812 	    statuses.child_errno = errno;
813 	IGNORE_RC(write(pfd[1], &statuses, sizeof(statuses)));
814 
815 	if (statuses.rehostentlen) {
816 	    /*
817 	     * Return our resulting rehostent through pipe...
818 	     */
819 	    IGNORE_RC(write(pfd[1], *rehostent, statuses.rehostentlen));
820 	    close(pfd[1]);
821 	    _exit(0);
822 	} else {
823 	    /*
824 	     * ...  or return error as exit code.
825 	     */
826 	    _exit(1);
827 	}
828     }
829 #if HAVE_SIGACTION
830     /*
831      * (parent) Restore signal mask to whatever it was before the fork.  -
832      * kw
833      */
834     sigprocmask(SIG_SETMASK, &old_sigset, NULL);
835 #endif /* HAVE_SIGACTION */
836 
837     /*
838      * (parent) Wait until lookup finishes, or interrupt, or cycled too
839      * many times (just in case) -BL
840      */
841 
842     close(pfd[1]);		/* parent won't use write side -BL */
843 
844     if (fpid < 0) {		/* fork failed */
845 	close(pfd[0]);
846 	goto failed;
847     }
848 
849     while (child_exited || (long) (time((time_t *) 0) - start_time) < dns_patience) {
850 
851 	FD_ZERO(&readfds);
852 	/*
853 	 * This allows us to abort immediately, not after 1-second timeout,
854 	 * when user hits abort key.  Can't do this when using SLANG (or at
855 	 * least I don't know how), so SLANG users must live with up-to-1s
856 	 * timeout.  -BL
857 	 *
858 	 * Whoops -- we need to make sure stdin is actually selectable!
859 	 * /dev/null isn't, on some systems, which makes some useful Lynx
860 	 * invocations fail.  -BL
861 	 */
862 	{
863 	    int kbd_fd = LYConsoleInputFD(TRUE);
864 
865 	    if (kbd_fd != INVSOC) {
866 		FD_SET(kbd_fd, &readfds);
867 	    }
868 	}
869 
870 	one_second.tv_sec = 1;
871 	one_second.tv_usec = 0;
872 	FD_SET(pfd[0], &readfds);
873 
874 	/*
875 	 * Return when data received, interrupted, or failed.  If nothing
876 	 * is waiting, we sleep for 1 second in select(), to be nice to the
877 	 * system.  -BL
878 	 */
879 #ifdef SOCKS
880 	if (socks_flag)
881 	    selret = Rselect(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
882 	else
883 #endif /* SOCKS */
884 	    selret = select(pfd[0] + 1, &readfds, NULL, NULL, &one_second);
885 
886 	if ((selret > 0) && FD_ISSET(pfd[0], &readfds)) {
887 	    /*
888 	     * First get status, including length of address.  -BL, kw
889 	     */
890 	    readret = read_bytes(pfd[0], (char *) &statuses, sizeof(statuses));
891 	    if (readret == sizeof(statuses)) {
892 		h_errno = statuses.child_h_errno;
893 		set_errno(statuses.child_errno);
894 #ifdef HAVE_H_ERRNO
895 		if (statuses.h_errno_valid) {
896 		    lynx_nsl_status = HT_H_ERRNO_VALID;
897 		    /*
898 		     * If something went wrong in the child process other
899 		     * than normal lookup errors, and it appears that we
900 		     * have enough info to know what went wrong, generate
901 		     * diagnostic output.  ENOMEM observed on linux in
902 		     * processes constrained with ulimit.  It would be too
903 		     * unkind to abort the session, access to local files
904 		     * or through a proxy may still work.  - kw
905 		     */
906 		    if (
907 #ifdef NETDB_INTERNAL		/* linux glibc: defined in netdb.h */
908 			   (errno && h_errno == NETDB_INTERNAL) ||
909 #endif
910 			   (errno == ENOMEM &&
911 			    statuses.rehostentlen == 0 &&
912 		    /* should probably be NETDB_INTERNAL if child
913 		       memory exhausted, but we may find that
914 		       h_errno remains unchanged. - kw */
915 			    h_errno == -2)) {
916 #ifndef MULTINET
917 			HTInetStatus("CHILD gethostbyname");
918 #endif
919 			HTAlert(LYStrerror(statuses.child_errno));
920 			if (errno == ENOMEM) {
921 			    /*
922 			     * Not much point in continuing, right?  Fake a
923 			     * 'z', should shorten pointless guessing
924 			     * cycle.  - kw
925 			     */
926 			    LYFakeZap(YES);
927 			}
928 		    }
929 		}
930 #endif /* HAVE_H_ERRNO */
931 		if (statuses.rehostentlen != 0) {
932 		    /*
933 		     * Then get the full reorganized hostent.  -BL, kw
934 		     */
935 		    if ((*rehostent = malloc(statuses.rehostentlen)) == 0)
936 			outofmem(__FILE__, "setup_nsl_fork");
937 		    readret = (*readit) (pfd[0], *rehostent, statuses.rehostentlen);
938 #ifdef DEBUG_HOSTENT
939 		    dumpit("Read from pipe", *rehostent);
940 #endif
941 		    if (readret == statuses.rehostentlen) {
942 			got_rehostent = 1;
943 			lynx_nsl_status = HT_OK;
944 		    } else if (!statuses.h_errno_valid) {
945 			lynx_nsl_status = HT_INTERNAL;
946 		    }
947 		}
948 	    } else {
949 		lynx_nsl_status = HT_ERROR;
950 	    }
951 	    /*
952 	     * Make sure child is cleaned up.  -BL
953 	     */
954 	    if (!child_exited)
955 		waitret = waitpid(fpid, &waitstat, WNOHANG);
956 	    if (!WIFEXITED(waitstat) && !WIFSIGNALED(waitstat)) {
957 		kill(fpid, SIGTERM);
958 		waitret = waitpid(fpid, &waitstat, WNOHANG);
959 	    }
960 	    break;
961 	}
962 
963 	/*
964 	 * Clean up if child exited before & no data received.  -BL
965 	 */
966 	if (child_exited) {
967 	    waitret = waitpid(fpid, &waitstat, WNOHANG);
968 	    break;
969 	}
970 	/*
971 	 * If child exited, loop once more looking for data.  -BL
972 	 */
973 	if ((waitret = waitpid(fpid, &waitstat, WNOHANG)) > 0) {
974 	    /*
975 	     * Data will be arriving right now, so make sure we don't
976 	     * short-circuit out for too many loops, and skip the interrupt
977 	     * check.  -BL
978 	     */
979 	    child_exited = 1;
980 	    continue;
981 	}
982 
983 	/*
984 	 * Abort if interrupt key pressed.
985 	 */
986 	if (HTCheckForInterrupt()) {
987 	    CTRACE((tfp, "LYGetHostByName: INTERRUPTED gethostbyname.\n"));
988 	    kill(fpid, SIGTERM);
989 	    waitpid(fpid, NULL, WNOHANG);
990 	    close(pfd[0]);
991 	    lynx_nsl_status = HT_INTERRUPTED;
992 	    return FALSE;
993 	}
994     }
995     close(pfd[0]);
996     if (waitret <= 0) {
997 	kill(fpid, SIGTERM);
998 	waitret = waitpid(fpid, &waitstat, WNOHANG);
999     }
1000     if (waitret > 0) {
1001 	if (WIFEXITED(waitstat)) {
1002 	    CTRACE((tfp,
1003 		    "LYGetHostByName: NSL_FORK child %d exited, status 0x%x.\n",
1004 		    (int) waitret, WEXITSTATUS(waitstat)));
1005 	} else if (WIFSIGNALED(waitstat)) {
1006 	    CTRACE((tfp,
1007 		    "LYGetHostByName: NSL_FORK child %d got signal, status 0x%x!\n",
1008 		    (int) waitret, WTERMSIG(waitstat)));
1009 #ifdef WCOREDUMP
1010 	    if (WCOREDUMP(waitstat)) {
1011 		CTRACE((tfp,
1012 			"LYGetHostByName: NSL_FORK child %d dumped core!\n",
1013 			(int) waitret));
1014 	    }
1015 #endif /* WCOREDUMP */
1016 	} else if (WIFSTOPPED(waitstat)) {
1017 	    CTRACE((tfp,
1018 		    "LYGetHostByName: NSL_FORK child %d is stopped, status 0x%x!\n",
1019 		    (int) waitret, WSTOPSIG(waitstat)));
1020 	}
1021     }
1022     if (!got_rehostent) {
1023 	goto failed;
1024     }
1025     return TRUE;
1026   failed:
1027     return FALSE;
1028 }
1029 
1030 /*
1031  * This is called via the child-side of the fork.
1032  */
really_gethostbyname(char * host,char * port GCC_UNUSED,STATUSES * statuses,void ** rehostent)1033 static void really_gethostbyname(char *host,
1034 				 char *port GCC_UNUSED,
1035 				 STATUSES * statuses,
1036 				 void **rehostent)
1037 {
1038     LYNX_HOSTENT *phost;	/* Pointer to host - See netdb.h */
1039     LYNX_HOSTENT *result = 0;
1040 
1041     (void) port;
1042 
1043     phost = gethostbyname(host);
1044     statuses->rehostentlen = 0;
1045     statuses->child_errno = errno;
1046     statuses->child_h_errno = h_errno;
1047 #ifdef HAVE_H_ERRNO
1048     statuses->h_errno_valid = YES;
1049 #endif
1050 #ifdef MVS
1051     CTRACE((tfp, "really_gethostbyname() returned %d\n", phost));
1052 #endif /* MVS */
1053 
1054 #ifdef DEBUG_HOSTENT_CHILD
1055     dump_hostent("CHILD gethostbyname", phost);
1056 #endif
1057     if (OK_HOST(phost)) {
1058 	statuses->rehostentlen = fill_rehostent(rehostent, phost);
1059 	result = (LYNX_HOSTENT *) (*rehostent);
1060 #ifdef DEBUG_HOSTENT_CHILD
1061 	dump_hostent("CHILD fill_rehostent", result);
1062 #endif
1063     }
1064     if (statuses->rehostentlen <= sizeof(LYNX_HOSTENT) || !OK_HOST(result)) {
1065 	statuses->rehostentlen = 0;
1066 	statuses->h_length = 0;
1067     } else {
1068 	statuses->h_length = result->h_length;
1069 #ifdef HAVE_H_ERRNO
1070 	if (h_errno == -2)	/* success, but h_errno unchanged? */
1071 	    statuses->h_errno_valid = NO;
1072 #endif
1073     }
1074 }
1075 #endif /* NSL_FORK */
1076 
1077 /*	Resolve an internet hostname, like gethostbyname
1078  *	------------------------------------------------
1079  *
1080  *  On entry,
1081  *	host	points to the given host name, not numeric address,
1082  *		without colon or port number.
1083  *
1084  *  On exit,
1085  *	returns a pointer to a LYNX_HOSTENT in static storage,
1086  *	or NULL in case of error or user interruption.
1087  *
1088  *  The interface is intended to be exactly the same as for (Unix)
1089  *  gethostbyname(), except for the following:
1090  *
1091  *  If NSL_FORK is not used, the result of gethostbyname is returned
1092  *  directly.  Otherwise:
1093  *  All lists, addresses, and strings referred to by pointers in the
1094  *  returned struct are located, together with the returned struct
1095  *  itself, in a buffer of size REHOSTENT_SIZE.  If not everything fits,
1096  *  some info is omitted, but the function is careful to still return
1097  *  a valid structure, without truncating strings; it tries to return,
1098  *  in order of decreasing priority, the first address (h_addr_list[0]), the
1099  *  official name (h_name), the additional addresses, then alias names.
1100  *
1101  *  If NULL is returned, the reason is made available in the global
1102  *  variable lynx_nsl_status, with one of the following values:
1103  *	HT_INTERRUPTED		Interrupted by user
1104  *	HT_NOT_ACCEPTABLE	Hostname detected as invalid
1105  *				(also sets h_errno)
1106  *	HT_H_ERRNO_VALID	An error occurred, and h_errno holds
1107  *				an appropriate value
1108  *	HT_ERROR		Resolver error, reason not known
1109  *	HT_INTERNAL		Internal error
1110  */
LYGetHostByName(char * host)1111 LYNX_HOSTENT *LYGetHostByName(char *host)
1112 {
1113 
1114 #ifdef NSL_FORK
1115     /* for transfer of result between from child to parent: */
1116     LYNX_HOSTENT *rehostent = 0;
1117 #endif /* NSL_FORK */
1118 
1119     LYNX_HOSTENT *result_phost = NULL;
1120 
1121 #ifdef __DJGPP__
1122     _resolve_hook = ResolveYield;
1123 #endif
1124 
1125     if (!host) {
1126 	CTRACE((tfp, "LYGetHostByName: Can't parse `NULL'.\n"));
1127 	lynx_nsl_status = HT_INTERNAL;
1128 	return NULL;
1129     }
1130     CTRACE((tfp, "LYGetHostByName: parsing `%s'.\n", host));
1131 
1132     /*  Could disable this if all our callers already check - kw */
1133     if (HTCheckForInterrupt()) {
1134 	CTRACE((tfp, "LYGetHostByName: INTERRUPTED for '%s'.\n", host));
1135 	lynx_nsl_status = HT_INTERRUPTED;
1136 	return NULL;
1137     }
1138 
1139     if (!valid_hostname(host)) {
1140 	lynx_nsl_status = HT_NOT_ACCEPTABLE;
1141 #ifdef NO_RECOVERY
1142 #ifdef _WINDOWS
1143 	WSASetLastError(NO_RECOVERY);
1144 #else
1145 	h_errno = NO_RECOVERY;
1146 #endif
1147 #endif
1148 	return NULL;
1149     }
1150 #ifdef MVS			/* Outstanding problem with crash in MVS gethostbyname */
1151     CTRACE((tfp, "LYGetHostByName: Calling gethostbyname(%s)\n", host));
1152 #endif /* MVS */
1153 
1154     CTRACE_FLUSH(tfp);		/* so child messages will not mess up parent log */
1155 
1156     lynx_nsl_status = HT_INTERNAL;	/* should be set to something else below */
1157 
1158 #ifdef NSL_FORK
1159     if (!setup_nsl_fork(really_gethostbyname,
1160 			read_hostent,
1161 			dump_hostent,
1162 			host, NULL, (void **) &rehostent)) {
1163 	goto failed;
1164     }
1165     result_phost = rehostent;
1166 #else /* Not NSL_FORK: */
1167 
1168 #ifdef _WINDOWS_NSL
1169     {
1170 	HANDLE hThread;
1171 	DWORD dwThreadID;
1172 
1173 #ifndef __CYGWIN__
1174 	if (!system_is_NT) {	/* for Windows9x */
1175 	    unsigned long t;
1176 
1177 	    t = (unsigned long) inet_addr(host);
1178 	    if (t != INADDR_NONE)
1179 		gbl_phost = gethostbyaddr((char *) &t, sizeof(t), AF_INET);
1180 	    else
1181 		gbl_phost = gethostbyname(host);
1182 	} else {		/* for Windows NT */
1183 #endif /* !__CYGWIN__ */
1184 	    gbl_phost = (LYNX_HOSTENT *) NULL;
1185 	    donelookup = FALSE;
1186 
1187 #if defined(__CYGWIN__) || defined(USE_WINSOCK2_H)
1188 	    SetLastError(WSAHOST_NOT_FOUND);
1189 #else
1190 	    WSASetLastError(WSAHOST_NOT_FOUND);
1191 #endif
1192 
1193 	    hThread = CreateThread(NULL, 4096UL, _fork_func, host, 0UL,
1194 				   &dwThreadID);
1195 	    if (!hThread)
1196 		MessageBox(NULL, "CreateThread",
1197 			   "CreateThread Failed", 0L);
1198 
1199 	    while (!donelookup) {
1200 		if (HTCheckForInterrupt()) {
1201 		    /* Note that host is a character array and is not freed */
1202 		    /* to avoid possible subthread problems: */
1203 		    if (!CloseHandle(hThread)) {
1204 			MessageBox((void *) NULL,
1205 				   "CloseHandle", "CloseHandle Failed", 0L);
1206 		    }
1207 		    lynx_nsl_status = HT_INTERRUPTED;
1208 		    return NULL;
1209 		}
1210 	    }
1211 #ifndef __CYGWIN__
1212 	}
1213 #endif /* !__CYGWIN__ */
1214 	if (gbl_phost) {
1215 	    lynx_nsl_status = HT_OK;
1216 	    result_phost = gbl_phost;
1217 	} else {
1218 	    lynx_nsl_status = HT_ERROR;
1219 	    goto failed;
1220 	}
1221     }
1222 
1223 #else /* !NSL_FORK, !_WINDOWS_NSL: */
1224     {
1225 	LYNX_HOSTENT *phost;
1226 
1227 	phost = gethostbyname(host);	/* See netdb.h */
1228 #ifdef MVS
1229 	CTRACE((tfp, "LYGetHostByName: gethostbyname() returned %d\n", phost));
1230 #endif /* MVS */
1231 	if (phost) {
1232 	    lynx_nsl_status = HT_OK;
1233 	    result_phost = phost;
1234 	} else {
1235 	    lynx_nsl_status = HT_H_ERRNO_VALID;
1236 	    goto failed;
1237 	}
1238     }
1239 #endif /* !NSL_FORK, !_WINDOWS_NSL */
1240 #endif /* !NSL_FORK */
1241 
1242 #ifdef DEBUG_HOSTENT
1243     dump_hostent("LYGetHostByName", result_phost);
1244     CTRACE((tfp, "LYGetHostByName: Resolved name to a hostent.\n"));
1245 #endif
1246 
1247     return result_phost;	/* OK */
1248 
1249   failed:
1250     CTRACE((tfp, "LYGetHostByName: Can't find internet node name `%s'.\n",
1251 	    host));
1252     return NULL;
1253 }
1254 
1255 /*	Parse a network node address and port
1256  *	-------------------------------------
1257  *
1258  *  On entry,
1259  *	str	points to a string with a node name or number,
1260  *		with optional trailing colon and port number.
1261  *	soc_in	points to the binary internet or decnet address field.
1262  *
1263  *  On exit,
1264  *	*soc_in is filled in.  If no port is specified in str, that
1265  *		field is left unchanged in *soc_in.
1266  */
1267 #ifndef INET6
HTParseInet(SockA * soc_in,const char * str)1268 static int HTParseInet(SockA * soc_in, const char *str)
1269 {
1270     char *port;
1271     int dotcount_ip = 0;	/* for dotted decimal IP addr */
1272     char *strptr;
1273     char *host = NULL;
1274 
1275     if (!str) {
1276 	CTRACE((tfp, "HTParseInet: Can't parse `NULL'.\n"));
1277 	return -1;
1278     }
1279     CTRACE((tfp, "HTParseInet: parsing `%s'.\n", str));
1280     if (HTCheckForInterrupt()) {
1281 	CTRACE((tfp, "HTParseInet: INTERRUPTED for '%s'.\n", str));
1282 	return -1;
1283     }
1284     StrAllocCopy(host, str);	/* Make a copy we can mutilate */
1285     /*
1286      * Parse port number if present.
1287      */
1288     if ((port = strchr(host, ':')) != NULL) {
1289 	*port++ = 0;		/* Chop off port */
1290 	strptr = port;
1291 	if (port[0] >= '0' && port[0] <= '9') {
1292 #ifdef UNIX
1293 	    soc_in->sin_port = (PortNumber) htons(strtol(port, &strptr, 10));
1294 #else /* VMS: */
1295 #ifdef DECNET
1296 	    soc_in->sdn_objnum = (unsigned char) (strtol(port, &strptr, 10));
1297 #else
1298 	    soc_in->sin_port = htons((PortNumber) strtol(port, &strptr, 10));
1299 #endif /* Decnet */
1300 #endif /* Unix vs. VMS */
1301 #ifdef SUPPRESS			/* 1. crashes!?!.  2. URL syntax has number not name */
1302 	} else {
1303 	    struct servent *serv = getservbyname(port, (char *) 0);
1304 
1305 	    if (serv) {
1306 		soc_in->sin_port = serv->s_port;
1307 	    } else {
1308 		CTRACE((tfp, "TCP: Unknown service %s\n", port));
1309 	    }
1310 #endif /* SUPPRESS */
1311 	}
1312 	if (strptr && *strptr != '\0') {
1313 	    FREE(host);
1314 	    HTAlwaysAlert(NULL, gettext("Address has invalid port"));
1315 	    return -1;
1316 	}
1317     }
1318 #ifdef DECNET
1319     /*
1320      * Read Decnet node name.  @@ Should know about DECnet addresses, but it's
1321      * probably worth waiting until the Phase transition from IV to V.
1322      */
1323     soc_in->sdn_nam.n_len = min(DN_MAXNAML, strlen(host));	/* <=6 in phase 4 */
1324     StrNCpy(soc_in->sdn_nam.n_name, host, soc_in->sdn_nam.n_len + 1);
1325     CTRACE((tfp,
1326 	    "DECnet: Parsed address as object number %d on host %.6s...\n",
1327 	    soc_in->sdn_objnum, host));
1328 #else /* parse Internet host: */
1329 
1330     if (*host >= '0' && *host <= '9') {		/* Test for numeric node address: */
1331 	strptr = host;
1332 	while (*strptr) {
1333 	    if (*strptr == '.') {
1334 		dotcount_ip++;
1335 	    } else if (!isdigit(UCH(*strptr))) {
1336 		break;
1337 	    }
1338 	    strptr++;
1339 	}
1340 	if (*strptr) {		/* found non-numeric, assume domain name */
1341 	    dotcount_ip = 0;
1342 	}
1343     }
1344 
1345     /*
1346      * Parse host number if present.
1347      */
1348     if (dotcount_ip == 3)	/* Numeric node address: */
1349     {
1350 #ifdef DGUX_OLD
1351 	soc_in->sin_addr.s_addr = inet_addr(host).s_addr;	/* See arpa/inet.h */
1352 #else
1353 #ifdef GUSI
1354 	soc_in->sin_addr = inet_addr(host);	/* See netinet/in.h */
1355 #else
1356 #ifdef HAVE_INET_ATON
1357 	if (!inet_aton(host, &(soc_in->sin_addr))) {
1358 	    CTRACE((tfp, "inet_aton(%s) returns error\n", host));
1359 	    FREE(host);
1360 	    return -1;
1361 	}
1362 #else
1363 	soc_in->sin_addr.s_addr = inet_addr(host);	/* See arpa/inet.h */
1364 #endif /* HAVE_INET_ATON */
1365 #endif /* GUSI */
1366 #endif /* DGUX_OLD */
1367 	FREE(host);
1368     } else {			/* Alphanumeric node name: */
1369 
1370 #ifdef MVS			/* Outstanding problem with crash in MVS gethostbyname */
1371 	CTRACE((tfp, "HTParseInet: Calling LYGetHostByName(%s)\n", host));
1372 #endif /* MVS */
1373 
1374 #ifdef _WINDOWS_NSL
1375 	gbl_phost = LYGetHostByName(host);	/* See above */
1376 	if (!gbl_phost)
1377 	    goto failed;
1378 	MemCpy((void *) &soc_in->sin_addr, gbl_phost->h_addr_list[0], gbl_phost->h_length);
1379 #else /* !_WINDOWS_NSL */
1380 	{
1381 	    LYNX_HOSTENT *phost;
1382 
1383 	    phost = LYGetHostByName(host);	/* See above */
1384 
1385 	    if (!phost)
1386 		goto failed;
1387 	    if (!phost)
1388 		goto failed;
1389 	    if (phost->h_length != sizeof soc_in->sin_addr) {
1390 		HTAlwaysAlert(host, gettext("Address length looks invalid"));
1391 	    }
1392 	    MemCpy((void *) &soc_in->sin_addr, phost->h_addr_list[0], phost->h_length);
1393 	}
1394 #endif /* _WINDOWS_NSL */
1395 
1396 	FREE(host);
1397     }				/* Alphanumeric node name */
1398 
1399     CTRACE((tfp,
1400 	    "HTParseInet: Parsed address as port %d, IP address %d.%d.%d.%d\n",
1401 	    (int) ntohs(soc_in->sin_port),
1402 	    (int) *((unsigned char *) (&soc_in->sin_addr) + 0),
1403 	    (int) *((unsigned char *) (&soc_in->sin_addr) + 1),
1404 	    (int) *((unsigned char *) (&soc_in->sin_addr) + 2),
1405 	    (int) *((unsigned char *) (&soc_in->sin_addr) + 3)));
1406 #endif /* Internet vs. Decnet */
1407 
1408     return 0;			/* OK */
1409 
1410   failed:
1411     CTRACE((tfp, "HTParseInet: Can't find internet node name `%s'.\n",
1412 	    host));
1413     FREE(host);
1414     switch (lynx_nsl_status) {
1415     case HT_NOT_ACCEPTABLE:
1416     case HT_INTERRUPTED:
1417 	return lynx_nsl_status;
1418     default:
1419 	return -1;
1420     }
1421 }
1422 #endif /* !INET6 */
1423 
1424 #ifdef INET6
1425 
dump_addrinfo(const char * tag,void * data)1426 static void dump_addrinfo(const char *tag, void *data)
1427 {
1428     LYNX_ADDRINFO *res;
1429     int count = 0;
1430 
1431     CTRACE((tfp, "dump_addrinfo %s:\n", tag));
1432     for (res = (LYNX_ADDRINFO *) data; res; res = res->ai_next) {
1433 	char hostbuf[1024], portbuf[1024];
1434 
1435 	++count;
1436 	hostbuf[0] = '\0';
1437 	portbuf[0] = '\0';
1438 	getnameinfo(res->ai_addr, res->ai_addrlen,
1439 		    hostbuf, (socklen_t) sizeof(hostbuf),
1440 		    portbuf, (socklen_t) sizeof(portbuf),
1441 		    NI_NUMERICHOST | NI_NUMERICSERV);
1442 
1443 	CTRACE((tfp,
1444 		"\t[%d] family %d, socktype %d, protocol %d addr %s port %s\n",
1445 		count,
1446 		res->ai_family,
1447 		res->ai_socktype,
1448 		res->ai_protocol,
1449 		hostbuf,
1450 		portbuf));
1451     }
1452 }
1453 
1454 #if defined(NSL_FORK)
1455 
1456 /*
1457  * Copy the relevant information (on the child-side).
1458  */
fill_addrinfo(void ** buffer,const LYNX_ADDRINFO * phost)1459 static size_t fill_addrinfo(void **buffer,
1460 			    const LYNX_ADDRINFO *phost)
1461 {
1462     const LYNX_ADDRINFO *q;
1463     LYNX_ADDRINFO *actual;
1464     LYNX_ADDRINFO *result;
1465     int count = 0;
1466     int limit = 0;
1467     size_t need = sizeof(LYNX_ADDRINFO);
1468     char *heap;
1469 
1470     CTRACE((tfp, "filladdr_info %p\n", (const void *) phost));
1471     for (q = phost; q != 0; q = q->ai_next) {
1472 	++limit;
1473 	need += phost->ai_addrlen;
1474 	need += sizeof(LYNX_ADDRINFO);
1475     }
1476     CTRACE((tfp, "...fill_addrinfo %d:%lu\n", limit, (unsigned long) need));
1477 
1478     if ((result = (LYNX_ADDRINFO *) calloc(1, need)) == 0)
1479 	outofmem(__FILE__, "fill_addrinfo");
1480 
1481     *buffer = actual = result;
1482     heap = ((char *) actual) + ((size_t) limit * sizeof(LYNX_ADDRINFO));
1483 
1484     for (count = 0; count < limit; ++count) {
1485 
1486 	/*
1487 	 * copying the whole structure seems simpler but because it is not
1488 	 * packed, uninitialized gaps make it hard to analyse with valgrind.
1489 	 */
1490 	/* *INDENT-EQLS* */
1491 	actual->ai_flags = phost->ai_flags;
1492 	actual->ai_family = phost->ai_family;
1493 	actual->ai_socktype = phost->ai_socktype;
1494 	actual->ai_protocol = phost->ai_protocol;
1495 	actual->ai_addrlen = phost->ai_addrlen;
1496 	actual->ai_addr = (struct sockaddr *) (void *) heap;
1497 
1498 	MemCpy(heap, phost->ai_addr, phost->ai_addrlen);
1499 	heap += phost->ai_addrlen;
1500 
1501 	phost = phost->ai_next;
1502 
1503 	actual->ai_next = ((count + 1 < limit)
1504 			   ? (actual + 1)
1505 			   : 0);
1506 	++actual;
1507     }
1508     return (size_t) (heap - (char *) result);
1509 }
1510 
1511 /*
1512  * Read data, repair pointers as done in fill_addrinfo().
1513  */
read_addrinfo(int fd,char * buffer,size_t length)1514 static unsigned read_addrinfo(int fd, char *buffer, size_t length)
1515 {
1516     unsigned result = read_bytes(fd, buffer, length);
1517     LYNX_ADDRINFO *actual = (LYNX_ADDRINFO *) (void *) buffer;
1518     LYNX_ADDRINFO *res;
1519     int count = 0;
1520     int limit;
1521     char *heap;
1522 
1523     CTRACE((tfp, "read_addrinfo length %lu\n", (unsigned long) length));
1524     for (limit = 0; actual[limit].ai_next; ++limit) {
1525     }
1526     ++limit;
1527     heap = (char *) (actual + limit);
1528     CTRACE((tfp, "...read_addrinfo %d items\n", limit));
1529 
1530     for (res = actual, count = 0; count < limit; ++count) {
1531 	res->ai_addr = (struct sockaddr *) (void *) heap;
1532 	heap += res->ai_addrlen;
1533 	if (count < limit - 1) {
1534 	    res->ai_next = (res + 1);
1535 	    ++res;
1536 	} else {
1537 	    res->ai_next = 0;
1538 	}
1539     }
1540 
1541 #ifdef DEBUG_HOSTENT
1542     dump_addrinfo("read_addrinfo", buffer);
1543 #endif
1544     return result;
1545 }
1546 
1547 /*
1548  * This is called via the child-side of the fork.
1549  */
really_getaddrinfo(char * host,char * port,STATUSES * statuses,void ** result)1550 static void really_getaddrinfo(char *host,
1551 			       char *port,
1552 			       STATUSES * statuses,
1553 			       void **result)
1554 {
1555     LYNX_ADDRINFO hints, *res;
1556     int error;
1557 
1558     memset(&hints, 0, sizeof(hints));
1559     hints.ai_family = PF_UNSPEC;
1560     hints.ai_socktype = SOCK_STREAM;
1561     error = getaddrinfo(host, port, &hints, &res);
1562     if (error || !res) {
1563 	CTRACE((tfp, "HTGetAddrInfo: getaddrinfo(%s, %s): %s\n", host, port,
1564 		gai_strerror(error)));
1565     } else {
1566 	statuses->child_errno = errno;
1567 	statuses->child_h_errno = h_errno;
1568 #ifdef HAVE_H_ERRNO
1569 	statuses->h_errno_valid = YES;
1570 #endif
1571 
1572 #ifdef DEBUG_HOSTENT_CHILD
1573 	dump_addrinfo("CHILD getaddrinfo", res);
1574 #endif
1575 	statuses->rehostentlen = fill_addrinfo(result, res);
1576 #ifdef DEBUG_HOSTENT_CHILD
1577 	dump_addrinfo("CHILD fill_addrinfo", (LYNX_ADDRINFO *) (*result));
1578 #endif
1579 	if (statuses->rehostentlen <= sizeof(LYNX_ADDRINFO)) {
1580 	    statuses->rehostentlen = 0;
1581 	    statuses->h_length = 0;
1582 	} else {
1583 	    statuses->h_length = (int) (((LYNX_ADDRINFO *) (*result))->ai_addrlen);
1584 	}
1585     }
1586 }
1587 #endif /* NSL_FORK */
1588 
HTGetAddrInfo(const char * str,const int defport)1589 static LYNX_ADDRINFO *HTGetAddrInfo(const char *str,
1590 				    const int defport)
1591 {
1592 #ifdef NSL_FORK
1593     /* for transfer of result between from child to parent: */
1594     void *readdrinfo = 0;
1595 
1596 #else
1597     LYNX_ADDRINFO hints;
1598     int error;
1599 #endif /* NSL_FORK */
1600     LYNX_ADDRINFO *res;
1601     char *p;
1602     char *s = NULL;
1603     char *host, *port;
1604     char pbuf[80];
1605 
1606     StrAllocCopy(s, str);
1607 
1608     if (s[0] == '[' && (p = strchr(s, ']')) != NULL) {
1609 	*p++ = '\0';
1610 	host = s + 1;
1611     } else {
1612 	p = s;
1613 	host = &s[0];
1614     }
1615     port = strrchr(p, ':');
1616     if (port) {
1617 	*port++ = '\0';
1618     } else {
1619 	sprintf(pbuf, "%d", defport);
1620 	port = pbuf;
1621     }
1622 
1623 #ifdef NSL_FORK
1624     if (setup_nsl_fork(really_getaddrinfo,
1625 		       read_addrinfo,
1626 		       dump_addrinfo,
1627 		       host, port, &readdrinfo)) {
1628 	res = readdrinfo;
1629     } else {
1630 	res = NULL;
1631     }
1632 #else
1633     memset(&hints, 0, sizeof(hints));
1634     hints.ai_family = PF_UNSPEC;
1635     hints.ai_socktype = SOCK_STREAM;
1636     error = getaddrinfo(host, port, &hints, &res);
1637     if (error || !res) {
1638 	CTRACE((tfp, "HTGetAddrInfo: getaddrinfo(%s, %s): %s\n", host, port,
1639 		gai_strerror(error)));
1640 	res = NULL;
1641     }
1642 #endif
1643 
1644     free(s);
1645 #ifdef DEBUG_HOSTENT
1646     dump_addrinfo("HTGetAddrInfo", res);
1647 #endif
1648     return res;
1649 }
1650 #endif /* INET6 */
1651 
1652 #ifdef LY_FIND_LEAKS
1653 /*	Free our name for the host on which we are - FM
1654  *	-------------------------------------------
1655  *
1656  */
free_HTTCP_hostname(void)1657 static void free_HTTCP_hostname(void)
1658 {
1659     FREE(hostname);
1660 }
1661 #endif /* LY_FIND_LEAKS */
1662 
1663 /*	Derive the name of the host on which we are
1664  *	-------------------------------------------
1665  *
1666  */
get_host_details(void)1667 static void get_host_details(void)
1668 {
1669     char name[MAXHOSTNAMELEN + 1];	/* The name of this host */
1670 
1671 #ifdef UCX
1672     char *domain_name;		/* The name of this host domain */
1673 #endif /* UCX */
1674 #ifdef NEED_HOST_ADDRESS	/* no -- needs name server! */
1675 #ifdef INET6
1676     LYNX_ADDRINFO hints, *res;
1677     int error;
1678 
1679 #else
1680     LYNX_HOSTENT *phost;	/* Pointer to host -- See netdb.h */
1681 #endif /* INET6 */
1682 #endif /* NEED_HOST_ADDRESS */
1683     size_t namelength = sizeof(name);
1684 
1685     if (hostname)
1686 	return;			/* Already done */
1687     gethostname(name, namelength);	/* Without domain */
1688     StrAllocCopy(hostname, name);
1689 #ifdef LY_FIND_LEAKS
1690     atexit(free_HTTCP_hostname);
1691 #endif
1692 #ifdef UCX
1693     /*
1694      * UCX doesn't give the complete domain name.  Get rest from UCX$BIND_DOM
1695      * logical.
1696      */
1697     if (strchr(hostname, '.') == NULL) {	/* Not full address */
1698 	domain_name = LYGetEnv("UCX$BIND_DOMAIN");
1699 	if (domain_name == NULL)
1700 	    domain_name = LYGetEnv("TCPIP$BIND_DOMAIN");
1701 	if (domain_name != NULL) {
1702 	    StrAllocCat(hostname, ".");
1703 	    StrAllocCat(hostname, domain_name);
1704 	}
1705     }
1706 #endif /* UCX */
1707     CTRACE((tfp, "TCP: Local host name is %s\n", hostname));
1708 
1709 #ifndef DECNET			/* Decnet ain't got no damn name server 8#OO */
1710 #ifdef NEED_HOST_ADDRESS	/* no -- needs name server! */
1711 #ifdef INET6
1712     memset(&hints, 0, sizeof(hints));
1713     hints.ai_family = PF_UNSPEC;
1714     hints.ai_socktype = SOCK_STREAM;
1715     hints.ai_flags = AI_CANONNAME;
1716     error = getaddrinfo(name, NULL, &hints, &res);
1717     if (error || !res || !res->ai_canonname) {
1718 	CTRACE((tfp, "TCP: %s: `%s'\n", gai_strerror(error), name));
1719 	if (res)
1720 	    freeaddrinfo(res);
1721 	return;			/* Fail! */
1722     }
1723     StrAllocCopy(hostname, res->ai_canonname);
1724     MemCpy(&HTHostAddress, res->ai_addr, res->ai_addrlen);
1725     freeaddrinfo(res);
1726 #else
1727     phost = gethostbyname(name);	/* See netdb.h */
1728     if (!OK_HOST(phost)) {
1729 	CTRACE((tfp,
1730 		"TCP: Can't find my own internet node address for `%s'!!\n",
1731 		name));
1732 	return;			/* Fail! */
1733     }
1734     StrAllocCopy(hostname, phost->h_name);
1735     MemCpy(&HTHostAddress, &phost->h_addr_list[0], phost->h_length);
1736 #endif /* INET6 */
1737     CTRACE((tfp, "     Name server says that I am `%s' = %s\n",
1738 	    hostname, HTInetString(&HTHostAddress)));
1739 #endif /* NEED_HOST_ADDRESS */
1740 
1741 #endif /* !DECNET */
1742 }
1743 
HTHostName(void)1744 const char *HTHostName(void)
1745 {
1746     get_host_details();
1747     return hostname;
1748 }
1749 
1750 #ifdef _WINDOWS
1751 #define SET_EINTR WSASetLastError(EINTR)
1752 #else
1753 #define SET_EINTR SOCKET_ERRNO = EINTR
1754 #endif
1755 
HTWasInterrupted(int * status)1756 static BOOL HTWasInterrupted(int *status)
1757 {
1758     BOOL result = FALSE;
1759 
1760     if (HTCheckForInterrupt()) {
1761 	result = TRUE;
1762 	*status = HT_INTERRUPTED;
1763 	SET_EINTR;
1764     }
1765     return result;
1766 }
1767 
1768 #define TRIES_PER_SECOND 10
1769 
1770 /*
1771  * Set the select-timeout to 0.1 seconds.
1772  */
set_timeout(struct timeval * timeoutp)1773 static void set_timeout(struct timeval *timeoutp)
1774 {
1775     timeoutp->tv_sec = 0;
1776     timeoutp->tv_usec = 100000;
1777 }
1778 
1779 #ifndef MULTINET		/* SOCKET_ERRNO != errno ? */
1780 #if !defined(UCX) || !defined(VAXC)	/* errno not modifiable ? */
1781 #define SOCKET_DEBUG_TRACE	/* show errno status after some system calls */
1782 #endif /* UCX && VAXC */
1783 #endif /* MULTINET */
1784 /*
1785  *  Interruptible connect as implemented for Mosaic by Marc Andreesen
1786  *  and hacked in for Lynx years ago by Lou Montulli, and further
1787  *  modified over the years by numerous Lynx lovers. - FM
1788  */
HTDoConnect(const char * url,const char * protocol,int default_port,int * s)1789 int HTDoConnect(const char *url,
1790 		const char *protocol,
1791 		int default_port,
1792 		int *s)
1793 {
1794     int status = 0;
1795     char *line = NULL;
1796     char *p1 = NULL;
1797     char *at_sign = NULL;
1798     char *host = NULL;
1799 
1800 #ifdef INET6
1801     LYNX_ADDRINFO *res = 0, *res0 = 0;
1802 
1803 #else
1804     struct sockaddr_in soc_address;
1805     struct sockaddr_in *soc_in = &soc_address;
1806 
1807     /*
1808      * Set up defaults.
1809      */
1810     memset(soc_in, 0, sizeof(*soc_in));
1811     soc_in->sin_family = AF_INET;
1812     soc_in->sin_port = htons((PortNumber) default_port);
1813 #endif /* INET6 */
1814 
1815     /*
1816      * Get node name and optional port number.
1817      */
1818     p1 = HTParse(url, "", PARSE_HOST);
1819     if ((at_sign = strchr(p1, '@')) != NULL) {
1820 	/*
1821 	 * If there's an @ then use the stuff after it as a hostname.
1822 	 */
1823 	StrAllocCopy(host, (at_sign + 1));
1824     } else {
1825 	StrAllocCopy(host, p1);
1826     }
1827     FREE(p1);
1828 
1829     HTSprintf0(&line, "%s%s", WWW_FIND_MESSAGE, host);
1830     _HTProgress(line);
1831 #ifdef INET6
1832     /* HTParseInet() is useless! */
1833     res0 = HTGetAddrInfo(host, default_port);
1834     if (res0 == NULL) {
1835 	HTSprintf0(&line, gettext("Unable to locate remote host %s."), host);
1836 	_HTProgress(line);
1837 	FREE(host);
1838 	FREE(line);
1839 	return HT_NO_DATA;
1840     }
1841 #else
1842     status = HTParseInet(soc_in, host);
1843     if (status) {
1844 	if (status != HT_INTERRUPTED) {
1845 	    if (status == HT_NOT_ACCEPTABLE) {
1846 		/* Not HTProgress, so warning won't be overwritten immediately;
1847 		 * but not HTAlert, because typically there will be other
1848 		 * alerts from the callers.  - kw
1849 		 */
1850 		HTUserMsg2(gettext("Invalid hostname %s"), host);
1851 	    } else {
1852 		HTSprintf0(&line,
1853 			   gettext("Unable to locate remote host %s."), host);
1854 		_HTProgress(line);
1855 	    }
1856 	    status = HT_NO_DATA;
1857 	}
1858 	FREE(host);
1859 	FREE(line);
1860 	return status;
1861     }
1862 #endif /* INET6 */
1863 
1864     HTSprintf0(&line, gettext("Making %s connection to %s"), protocol, host);
1865     _HTProgress(line);
1866     FREE(host);
1867     FREE(line);
1868 
1869     /*
1870      * Now, let's get a socket set up from the server for the data.
1871      */
1872 #ifndef INET6
1873     *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1874     if (*s == -1) {
1875 	HTAlert(gettext("socket failed."));
1876 	return HT_NO_DATA;
1877     }
1878 #else
1879     for (res = res0; res; res = res->ai_next) {
1880 	*s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1881 	if (*s == -1) {
1882 	    char hostbuf[1024], portbuf[1024];
1883 
1884 	    getnameinfo(res->ai_addr, res->ai_addrlen,
1885 			hostbuf, (socklen_t) sizeof(hostbuf),
1886 			portbuf, (socklen_t) sizeof(portbuf),
1887 			NI_NUMERICHOST | NI_NUMERICSERV);
1888 	    HTSprintf0(&line,
1889 		       gettext("socket failed: family %d addr %s port %s."),
1890 		       res->ai_family, hostbuf, portbuf);
1891 	    _HTProgress(line);
1892 	    FREE(line);
1893 	    continue;
1894 	}
1895 #endif /* INET6 */
1896 
1897 #if !defined(DOSPATH) || defined(__DJGPP__)
1898 #if !defined(NO_IOCTL) || defined(USE_FCNTL)
1899 	/*
1900 	 * Make the socket non-blocking, so the connect can be canceled.  This
1901 	 * means that when we issue the connect we should NOT have to wait for
1902 	 * the accept on the other end.
1903 	 */
1904 	{
1905 #ifdef USE_FCNTL
1906 	    int ret = fcntl(*s, F_SETFL, O_NONBLOCK);
1907 
1908 #else
1909 	    int val = 1;
1910 	    int ret = IOCTL(*s, FIONBIO, &val);
1911 #endif /* USE_FCNTL */
1912 	    if (ret == -1)
1913 		_HTProgress(gettext("Could not make connection non-blocking."));
1914 	}
1915 #endif /* !NO_IOCTL || USE_FCNTL */
1916 #endif /* !DOSPATH || __DJGPP__ */
1917 
1918 	/*
1919 	 * Issue the connect.  Since the server can't do an instantaneous
1920 	 * accept and we are non-blocking, this will almost certainly return a
1921 	 * negative status.
1922 	 */
1923 #ifdef SOCKS
1924 	if (socks_flag) {
1925 #ifdef INET6
1926 	    status = Rconnect(*s, res->ai_addr, res->ai_addrlen);
1927 #else
1928 	    status = Rconnect(*s, (struct sockaddr *) &soc_address,
1929 			      sizeof(soc_address));
1930 #ifndef SHORTENED_RBIND
1931 	    socks_bind_remoteAddr = soc_address.sin_addr.s_addr;
1932 #endif
1933 #endif /* INET6 */
1934 	} else
1935 #endif /* SOCKS */
1936 #ifdef INET6
1937 	    status = connect(*s, res->ai_addr, res->ai_addrlen);
1938 #else
1939 	    status = connect(*s, (struct sockaddr *) &soc_address, sizeof(soc_address));
1940 #endif /* INET6 */
1941 
1942 	/*
1943 	 * According to the Sun man page for connect:
1944 	 *  EINPROGRESS         The socket is non-blocking and the  con-
1945 	 *                      nection cannot be completed immediately.
1946 	 *                      It is possible to select(2) for  comple-
1947 	 *                      tion  by  selecting the socket for writ-
1948 	 *                      ing.
1949 	 * According to the Motorola SVR4 man page for connect:
1950 	 *  EAGAIN              The socket is non-blocking and the  con-
1951 	 *                      nection cannot be completed immediately.
1952 	 *                      It is possible to select for  completion
1953 	 *                      by  selecting  the  socket  for writing.
1954 	 *                      However, this is only  possible  if  the
1955 	 *                      socket  STREAMS  module  is  the topmost
1956 	 *                      module on  the  protocol  stack  with  a
1957 	 *                      write  service  procedure.  This will be
1958 	 *                      the normal case.
1959 	 */
1960 	if ((status < 0) &&
1961 	    (SOCKET_ERRNO == EINPROGRESS
1962 #ifdef EAGAIN
1963 	     || SOCKET_ERRNO == EAGAIN
1964 #endif
1965 	    )) {
1966 	    struct timeval select_timeout;
1967 	    int ret;
1968 	    int tries = 0;
1969 
1970 #ifdef SOCKET_DEBUG_TRACE
1971 	    HTInetStatus("this socket's first connect");
1972 #endif /* SOCKET_DEBUG_TRACE */
1973 	    ret = 0;
1974 	    while (ret <= 0) {
1975 		fd_set writefds;
1976 
1977 		/*
1978 		 * Protect against an infinite loop.
1979 		 */
1980 		if ((tries++ / TRIES_PER_SECOND) >= connect_timeout) {
1981 		    HTAlert(gettext("Connection failed (too many retries)."));
1982 #ifdef INET6
1983 		    FREE(line);
1984 #ifndef NSL_FORK
1985 		    if (res0)
1986 			freeaddrinfo(res0);
1987 #endif
1988 #endif /* INET6 */
1989 		    return HT_NO_DATA;
1990 		}
1991 		set_timeout(&select_timeout);
1992 		FD_ZERO(&writefds);
1993 		FD_SET((unsigned) *s, &writefds);
1994 #ifdef SOCKS
1995 		if (socks_flag)
1996 		    ret = Rselect(*s + 1, NULL,
1997 				  &writefds, NULL, &select_timeout);
1998 		else
1999 #endif /* SOCKS */
2000 		    ret = select(*s + 1,
2001 				 NULL,
2002 				 &writefds,
2003 				 NULL,
2004 				 &select_timeout);
2005 
2006 #ifdef SOCKET_DEBUG_TRACE
2007 		if (tries == 1) {
2008 		    HTInetStatus("this socket's first select");
2009 		}
2010 #endif /* SOCKET_DEBUG_TRACE */
2011 		/*
2012 		 * If we suspend, then it is possible that select will be
2013 		 * interrupted.  Allow for this possibility.  - JED
2014 		 */
2015 		if ((ret == -1) && (errno == EINTR))
2016 		    continue;
2017 
2018 #ifdef SOCKET_DEBUG_TRACE
2019 		if (ret < 0) {
2020 		    HTInetStatus("failed select");
2021 		}
2022 #endif /* SOCKET_DEBUG_TRACE */
2023 		/*
2024 		 * Again according to the Sun and Motorola man pages for
2025 		 * connect:
2026 		 *  EALREADY    The socket is non-blocking and a  previ-
2027 		 *              ous  connection attempt has not yet been
2028 		 *              completed.
2029 		 * Thus if the SOCKET_ERRNO is NOT EALREADY we have a real
2030 		 * error, and should break out here and return that error.
2031 		 * Otherwise if it is EALREADY keep on trying to complete the
2032 		 * connection.
2033 		 */
2034 		if ((ret < 0) && (SOCKET_ERRNO != EALREADY)) {
2035 		    status = ret;
2036 		    break;
2037 		} else if (ret > 0) {
2038 		    /*
2039 		     * Extra check here for connection success, if we try to
2040 		     * connect again, and get EISCONN, it means we have a
2041 		     * successful connection.  But don't check with SOCKS.
2042 		     */
2043 #ifdef SOCKS
2044 		    if (socks_flag) {
2045 			status = 0;
2046 		    } else {
2047 #endif /* SOCKS */
2048 #ifdef INET6
2049 			status = connect(*s, res->ai_addr, res->ai_addrlen);
2050 #else
2051 			status = connect(*s, (struct sockaddr *) &soc_address,
2052 					 sizeof(soc_address));
2053 #endif /* INET6 */
2054 #ifdef UCX
2055 			/*
2056 			 * A UCX feature:  Instead of returning EISCONN UCX
2057 			 * returns EADDRINUSE.  Test for this status also.
2058 			 */
2059 			if ((status < 0) && ((SOCKET_ERRNO == EISCONN) ||
2060 					     (SOCKET_ERRNO == EADDRINUSE)))
2061 #else
2062 			if ((status < 0) && (SOCKET_ERRNO == EISCONN))
2063 #endif /* UCX */
2064 			{
2065 			    status = 0;
2066 			}
2067 
2068 			if (status && (SOCKET_ERRNO == EALREADY))	/* new stuff LJM */
2069 			    ret = 0;	/* keep going */
2070 			else {
2071 #ifdef SOCKET_DEBUG_TRACE
2072 			    if (status < 0) {
2073 				HTInetStatus("confirm-ready connect");
2074 			    }
2075 #endif /* SOCKET_DEBUG_TRACE */
2076 			    break;
2077 			}
2078 #ifdef SOCKS
2079 		    }
2080 #endif /* SOCKS */
2081 		}
2082 #ifdef SOCKS
2083 		else if (!socks_flag)
2084 #else
2085 		else
2086 #endif /* SOCKS */
2087 		{
2088 		    /*
2089 		     * The select says we aren't ready yet.  Try to connect
2090 		     * again to make sure.  If we don't get EALREADY or
2091 		     * EISCONN, something has gone wrong.  Break out and report
2092 		     * it.
2093 		     *
2094 		     * For some reason, SVR4 returns EAGAIN here instead of
2095 		     * EALREADY, even though the man page says it should be
2096 		     * EALREADY.
2097 		     *
2098 		     * For some reason, UCX pre 3 apparently returns errno =
2099 		     * 18242 instead of EALREADY or EISCONN.
2100 		     */
2101 #ifdef INET6
2102 		    status = connect(*s, res->ai_addr, res->ai_addrlen);
2103 #else
2104 		    status = connect(*s, (struct sockaddr *) &soc_address,
2105 				     sizeof(soc_address));
2106 #endif /* INET6 */
2107 		    if ((status < 0) &&
2108 			(SOCKET_ERRNO != EALREADY
2109 #ifdef EAGAIN
2110 			 && SOCKET_ERRNO != EAGAIN
2111 #endif
2112 			) &&
2113 #ifdef UCX
2114 			(SOCKET_ERRNO != 18242) &&
2115 #endif /* UCX */
2116 			(SOCKET_ERRNO != EISCONN)) {
2117 #ifdef SOCKET_DEBUG_TRACE
2118 			HTInetStatus("confirm-not-ready connect");
2119 #endif /* SOCKET_DEBUG_TRACE */
2120 			break;
2121 		    }
2122 		}
2123 		if (HTWasInterrupted(&status)) {
2124 		    CTRACE((tfp, "*** INTERRUPTED in middle of connect.\n"));
2125 		    break;
2126 		}
2127 	    }
2128 	}
2129 #ifdef SOCKET_DEBUG_TRACE
2130 	else if (status < 0) {
2131 	    HTInetStatus("this socket's first and only connect");
2132 	}
2133 #endif /* SOCKET_DEBUG_TRACE */
2134 #ifdef INET6
2135 	if (status < 0) {
2136 	    NETCLOSE(*s);
2137 	    *s = -1;
2138 	    continue;
2139 	}
2140 	break;
2141     }
2142 #endif /* INET6 */
2143 
2144 #ifdef INET6
2145     if (*s < 0)
2146 #else
2147     if (status < 0)
2148 #endif /* INET6 */
2149     {
2150 	/*
2151 	 * The connect attempt failed or was interrupted, so close up the
2152 	 * socket.
2153 	 */
2154 	NETCLOSE(*s);
2155     }
2156 #if !defined(DOSPATH) || defined(__DJGPP__)
2157 #if !defined(NO_IOCTL) || defined(USE_FCNTL)
2158     else {
2159 	/*
2160 	 * Make the socket blocking again on good connect.
2161 	 */
2162 #ifdef USE_FCNTL
2163 	int ret = fcntl(*s, F_SETFL, 0);
2164 
2165 #else
2166 	int val = 0;
2167 	int ret = IOCTL(*s, FIONBIO, &val);
2168 #endif /* USE_FCNTL */
2169 	if (ret == -1)
2170 	    _HTProgress(gettext("Could not restore socket to blocking."));
2171     }
2172 #endif /* !NO_IOCTL || USE_FCNTL */
2173 #endif /* !DOSPATH || __DJGPP__ */
2174 
2175 #ifdef INET6
2176     FREE(line);
2177 #ifndef NSL_FORK
2178     if (res0)
2179 	freeaddrinfo(res0);
2180 #endif
2181 #endif /* INET6 */
2182     return status;
2183 }
2184 
2185 /*
2186  *  This is so interruptible reads can be implemented cleanly.
2187  */
HTDoRead(int fildes,void * buf,unsigned nbyte)2188 int HTDoRead(int fildes,
2189 	     void *buf,
2190 	     unsigned nbyte)
2191 {
2192     int result;
2193     BOOL ready;
2194 
2195 #if !defined(NO_IOCTL)
2196     int ret;
2197     fd_set readfds;
2198     struct timeval select_timeout;
2199     int tries = 0;
2200 
2201 #ifdef USE_READPROGRESS
2202     int otries = 0;
2203     time_t otime = time((time_t *) 0);
2204     time_t start = otime;
2205 #endif
2206 #endif /* !NO_IOCTL */
2207 
2208 #if defined(UNIX) && !defined(__BEOS__)
2209     if (fildes == 0) {
2210 	/*
2211 	 * 0 can be a valid socket fd, but if it's a tty something must have
2212 	 * gone wrong.  - kw
2213 	 */
2214 	if (isatty(fildes)) {
2215 	    CTRACE((tfp, "HTDoRead - refusing to read fd 0 which is a tty!\n"));
2216 	    return -1;
2217 	}
2218     } else
2219 #endif
2220     if (fildes <= 0) {
2221 	CTRACE((tfp, "HTDoRead - no file descriptor!\n"));
2222 	return -1;
2223     }
2224 
2225     if (HTWasInterrupted(&result)) {
2226 	CTRACE((tfp, "HTDoRead - interrupted before starting!\n"));
2227 	return (result);
2228     }
2229 #if defined(NO_IOCTL)
2230     ready = TRUE;
2231 #else
2232     ready = FALSE;
2233     while (!ready) {
2234 	/*
2235 	 * Protect against an infinite loop.
2236 	 */
2237 	if ((tries++ / TRIES_PER_SECOND) >= reading_timeout) {
2238 	    HTAlert(gettext("Socket read failed (too many tries)."));
2239 	    SET_EINTR;
2240 	    result = HT_INTERRUPTED;
2241 	    break;
2242 	}
2243 #ifdef USE_READPROGRESS
2244 	if (tries - otries > TRIES_PER_SECOND) {
2245 	    time_t t = time((time_t *) 0);
2246 
2247 	    otries = tries;
2248 	    if (t - otime >= 5) {
2249 		otime = t;
2250 		HTReadProgress((off_t) (-1), (off_t) 0);	/* Put "stalled" message */
2251 	    }
2252 	}
2253 #endif
2254 
2255 	/*
2256 	 * If we suspend, then it is possible that select will be interrupted.
2257 	 * Allow for this possibility.  - JED
2258 	 */
2259 	do {
2260 	    set_timeout(&select_timeout);
2261 	    FD_ZERO(&readfds);
2262 	    FD_SET((unsigned) fildes, &readfds);
2263 #ifdef SOCKS
2264 	    if (socks_flag)
2265 		ret = Rselect(fildes + 1,
2266 			      &readfds, NULL, NULL, &select_timeout);
2267 	    else
2268 #endif /* SOCKS */
2269 		ret = select(fildes + 1,
2270 			     &readfds, NULL, NULL, &select_timeout);
2271 	} while ((ret == -1) && (errno == EINTR));
2272 
2273 	if (ret < 0) {
2274 	    result = -1;
2275 	    break;
2276 	} else if (ret > 0) {
2277 	    ready = TRUE;
2278 	} else if (HTWasInterrupted(&result)) {
2279 	    break;
2280 	}
2281     }
2282 #endif /* !NO_IOCTL */
2283 
2284     if (ready) {
2285 #if defined(UCX) && defined(VAXC)
2286 	/*
2287 	 * VAXC and UCX problem only.
2288 	 */
2289 	errno = vaxc$errno = 0;
2290 	result = SOCKET_READ(fildes, buf, nbyte);
2291 	CTRACE((tfp,
2292 		"Read - result,errno,vaxc$errno: %d %d %d\n", result, errno, vaxc$errno));
2293 	if ((result <= 0) && TRACE)
2294 	    perror("HTTCP.C:HTDoRead:read");	/* RJF */
2295 	/*
2296 	 * An errno value of EPIPE and result < 0 indicates end-of-file on VAXC.
2297 	 */
2298 	if ((result <= 0) && (errno == EPIPE)) {
2299 	    result = 0;
2300 	    set_errno(0);
2301 	}
2302 #else
2303 #ifdef UNIX
2304 	while ((result = (int) SOCKET_READ(fildes, buf, nbyte)) == -1) {
2305 	    if (errno == EINTR)
2306 		continue;
2307 #ifdef ERESTARTSYS
2308 	    if (errno == ERESTARTSYS)
2309 		continue;
2310 #endif /* ERESTARTSYS */
2311 	    HTInetStatus("read");
2312 	    break;
2313 	}
2314 #else /* UNIX */
2315 	result = SOCKET_READ(fildes, buf, nbyte);
2316 #endif /* !UNIX */
2317 #endif /* UCX && VAXC */
2318     }
2319 #ifdef USE_READPROGRESS
2320     CTRACE2(TRACE_TIMING, (tfp, "...HTDoRead returns %d (%" PRI_time_t
2321 			   " seconds)\n",
2322 			   result, CAST_time_t (time((time_t *)0) - start)));
2323 #endif
2324     return result;
2325 }
2326 
2327 #ifdef SVR4_BSDSELECT
2328 /*
2329  *  This is a fix for the difference between BSD's select() and
2330  *  SVR4's select().  SVR4's select() can never return a value larger
2331  *  than the total number of file descriptors being checked.  So, if
2332  *  you select for read and write on one file descriptor, and both
2333  *  are true, SVR4 select() will only return 1.  BSD select in the
2334  *  same situation will return 2.
2335  *
2336  *	Additionally, BSD select() on timing out, will zero the masks,
2337  *	while SVR4 does not.  This is fixed here as well.
2338  *
2339  *	Set your tabstops to 4 characters to have this code nicely formatted.
2340  *
2341  *	Jerry Whelan, guru@bradley.edu, June 12th, 1993
2342  */
2343 #ifdef select
2344 #undef select
2345 #endif /* select */
2346 
2347 #ifdef SOCKS
2348 #ifdef Rselect
2349 #undef Rselect
2350 #endif /* Rselect */
2351 #endif /* SOCKS */
2352 
2353 #include <sys/types.h>
2354 #include <sys/time.h>
2355 #include <sys/select.h>
2356 
BSDselect(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * select_timeout)2357 int BSDselect(int nfds,
2358 	      fd_set * readfds,
2359 	      fd_set * writefds,
2360 	      fd_set * exceptfds,
2361 	      struct timeval *select_timeout)
2362 {
2363     int rval, i;
2364 
2365 #ifdef SOCKS
2366     if (socks_flag)
2367 	rval = Rselect(nfds, readfds, writefds, exceptfds, select_timeout);
2368     else
2369 #endif /* SOCKS */
2370 	rval = select(nfds, readfds, writefds, exceptfds, select_timeout);
2371 
2372     switch (rval) {
2373     case -1:
2374 	return (rval);
2375 
2376     case 0:
2377 	if (readfds != NULL)
2378 	    FD_ZERO(readfds);
2379 	if (writefds != NULL)
2380 	    FD_ZERO(writefds);
2381 	if (exceptfds != NULL)
2382 	    FD_ZERO(exceptfds);
2383 	return (rval);
2384 
2385     default:
2386 	for (i = 0, rval = 0; i < nfds; i++) {
2387 	    if ((readfds != NULL) && FD_ISSET(i, readfds))
2388 		rval++;
2389 	    if ((writefds != NULL) && FD_ISSET(i, writefds))
2390 		rval++;
2391 	    if ((exceptfds != NULL) && FD_ISSET(i, exceptfds))
2392 		rval++;
2393 
2394 	}
2395 	return (rval);
2396     }
2397 /* Should never get here */
2398 }
2399 #endif /* SVR4_BSDSELECT */
2400