$NetBSD: patch-ao,v 1.1 2000/01/15 17:44:24 hubertf Exp $ diff -x *.orig -urN ./WWW/Library/Implementation/HTTCP.c /usr/pkgsrc/www/lynx/work.unpatched/lynx2-8-2/WWW/Library/Implementation/HTTCP.c --- ./WWW/Library/Implementation/HTTCP.c Mon May 24 21:00:53 1999 +++ /usr/pkgsrc/www/lynx/work.unpatched/lynx2-8-2/WWW/Library/Implementation/HTTCP.c Sat Jan 15 07:57:18 2000 @@ -297,13 +297,15 @@ PUBLIC CONST char * HTInetString ARGS1( SockA*, soc_in) { - static char string[16]; - 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; + static char hostbuf[MAXHOSTNAMELEN]; + getnameinfo((struct sockaddr *)soc_in, +#ifdef SIN6_LEN + ((struct sockaddr *)soc_in)->sa_len, +#else + SA_LEN((struct sockaddr *)soc_in), +#endif + hostbuf, sizeof(hostbuf), NULL, 0, NI_NUMERICHOST); + return hostbuf; } #endif /* !DECNET */ @@ -1051,11 +1053,13 @@ ** *soc_in is filled in. If no port is specified in str, that ** field is left unchanged in *soc_in. */ -PUBLIC int HTParseInet ARGS2( +PUBLIC int HTParseInet ARGS3( SockA *, soc_in, - CONST char *, str) + CONST char *, str, + int, default_port) { char *port; + char portstr[NI_MAXSERV]; int dotcount_ip = 0; /* for dotted decimal IP addr */ #ifndef _WINDOWS_NSL char *host = NULL; @@ -1078,28 +1082,18 @@ /* ** Parse port number if present. */ - if ((port = strchr(host, ':')) != NULL) { - *port++ = 0; /* Chop off port */ - if (port[0] >= '0' && port[0] <= '9') { -#ifdef unix - soc_in->sin_port = htons(atol(port)); -#else /* VMS: */ -#ifdef DECNET - soc_in->sdn_objnum = (unsigned char)(strtol(port, (char**)0, 10)); -#else - soc_in->sin_port = htons((unsigned short)strtol(port,(char**)0,10)); -#endif /* Decnet */ -#endif /* Unix vs. VMS */ -#ifdef SUPPRESS /* 1. crashes!?!. 2. Not recommended */ - } else { - struct servent * serv = getservbyname(port, (char*)0); - if (serv) { - soc_in->sin_port = serv->s_port; - } else { - CTRACE(tfp, "TCP: Unknown service %s\n", port); - } -#endif /* SUPPRESS */ - } + + if (!strrchr(host, ']')) + port = strrchr(host, ':'); + else + port = strrchr(strrchr(host, ']'), ':'); + + if (port) { + *port++ = 0; /* Chop off port */ + } + else { + sprintf(portstr,"%d", default_port); + port = portstr; } #ifdef DECNET @@ -1113,6 +1107,13 @@ soc_in->sdn_objnum, host); #else /* parse Internet host: */ + /* [host] case */ + if (host[0] == '[' && host[strlen(host) - 1] == ']') { + host[strlen(host) - 1] = '\0'; + host++; + } + +#ifndef INET6 if (*host >= '0' && *host <= '9') { /* Test for numeric node address: */ char *strptr = host; while (*strptr) { @@ -1127,11 +1128,14 @@ dotcount_ip = 0; } } +#endif /* ** Parse host number if present. */ - if (dotcount_ip == 3) { /* Numeric node address: */ +#ifndef INET6 + if (dotcount_ip == 3) /* Numeric node address: */ + { #ifdef DJGPP soc_in->sin_addr.s_addr = htonl(aton(host)); @@ -1159,7 +1163,9 @@ #ifndef _WINDOWS_NSL FREE(host); #endif /* _WINDOWS_NSL */ - } else { /* Alphanumeric node name: */ + } else +#endif + { /* Alphanumeric node name: */ #ifdef MVS /* Outstanding problem with crash in MVS gethostbyname */ CTRACE(tfp, "HTParseInet: Calling LYGetHostByName(%s)\n", host); @@ -1181,10 +1187,18 @@ memcpy((void *)&soc_in->sin_addr, phost->h_addr, phost->h_length); #else /* !DJGPP, !_WINDOWS_NSL: */ { - struct hostent *phost; - phost = LYGetHostByName(host); /* See above */ + struct addrinfo hints, *res; + int error; - if (!phost) goto failed; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + error = getaddrinfo(host, port, &hints, &res); + + if (error || !res) { + CTRACE(tfp, "HTParseInet: getaddrinfo(%s): %s\n", host, + gai_strerror(error)); + goto failed; + } #if defined(VMS) && defined(CMU_TCP) /* ** In LIBCMU, phost->h_length contains not the length of one address @@ -1194,14 +1208,18 @@ ** longer supported, and CMU users are encouraged to obtain and use ** SOCKETSHR/NETLIB instead. - S. Bjorndahl */ - memcpy((void *)&soc_in->sin_addr, phost->h_addr, 4); -#else - if (!phost) goto failed; - if (phost->h_length != sizeof soc_in->sin_addr) { - HTAlwaysAlert(host, gettext("Address length looks invalid")); + if (res->ai_family == AF_INET) { + memcpy((void *)&soc_in->sin_addr, + &((struct sockaddr_in *)res->ai_addr)->sin_addr, 4); + } else { + CTRACE(tfp, "HTParseInet: unsupported address family %d\n", + res->ai_family); + goto failed; } - memcpy((void *)&soc_in->sin_addr, phost->h_addr, phost->h_length); +#else + memcpy((void *)soc_in, res->ai_addr, res->ai_addrlen); #endif /* VMS && CMU_TCP */ + freeaddrinfo(res); } #endif /* !DJGPP, !_WINDOWS_NSL */ #endif /* !DJGPP */ @@ -1211,12 +1229,14 @@ } /* Alphanumeric node name */ +#ifndef INET6 CTRACE(tfp, "HTParseInet: Parsed address as port %d, IP address %d.%d.%d.%d\n", (int)ntohs(soc_in->sin_port), (int)*((unsigned char *)(&soc_in->sin_addr)+0), (int)*((unsigned char *)(&soc_in->sin_addr)+1), (int)*((unsigned char *)(&soc_in->sin_addr)+2), (int)*((unsigned char *)(&soc_in->sin_addr)+3)); +#endif #endif /* Internet vs. Decnet */ return 0; /* OK */ @@ -1232,8 +1252,50 @@ case HT_INTERRUPTED: return lynx_nsl_status; default: - return -1; + return -1; + } } + +PRIVATE struct addrinfo * +HTGetAddrInfo ARGS2( + CONST char *, str, + CONST int, defport) +{ + struct addrinfo hints, *res; + int error; + char *p; + char *s; + char *host, *port; + char pbuf[10]; + + s = strdup(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 { + snprintf(pbuf, sizeof(pbuf), "%d", defport); + port = pbuf; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, port, &hints, &res); + if (error || !res) { + CTRACE(tfp, "HTGetAddrInfo: getaddrinfo(%s, %s): %s\n", host, port, + gai_strerror(error)); + return NULL; + } + + return res; } #ifdef LY_FIND_LEAKS @@ -1262,7 +1324,8 @@ char *domain_name; /* The name of this host domain */ #endif /* UCX */ #ifdef NEED_HOST_ADDRESS /* no -- needs name server! */ - struct hostent * phost; /* Pointer to host -- See netdb.h */ + struct addrinfo hints, *res; + int error; #endif /* NEED_HOST_ADDRESS */ int namelength = sizeof(name); @@ -1290,14 +1353,20 @@ #ifndef DECNET /* Decnet ain't got no damn name server 8#OO */ #ifdef NEED_HOST_ADDRESS /* no -- needs name server! */ - 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); + 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, phost->h_name); - memcpy(&HTHostAddress, &phost->h_addr, phost->h_length); + StrAllocCopy(hostname, res->ai_canonname); + memcpy(&HTHostAddress, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); CTRACE(tfp, " Name server says that I am `%s' = %s\n", hostname, HTInetString(&HTHostAddress)); #endif /* NEED_HOST_ADDRESS */ @@ -1322,20 +1391,14 @@ int, default_port, int *, s) { - struct sockaddr_in soc_address; - struct sockaddr_in *soc_in = &soc_address; int status; char *line = NULL; char *p1 = NULL; char *at_sign = NULL; char *host = NULL; - - /* - ** Set up defaults. - */ - memset(soc_in, 0, sizeof(*soc_in)); - soc_in->sin_family = AF_INET; - soc_in->sin_port = htons(default_port); + int error; + struct sockaddr *sa; + struct addrinfo *res, *res0; /* ** Get node name and optional port number. @@ -1353,25 +1416,15 @@ HTSprintf0 (&line, gettext("Looking up %s."), host); _HTProgress (line); - 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; - } + /* HTParseInet() is useless! */ + _HTProgress(host); + res0 = HTGetAddrInfo(host, default_port); + if (res0 == NULL) { + sprintf (line, "Unable to locate remote host %s.", host); + _HTProgress(line); FREE(host); FREE(line); - return status; + return HT_NO_DATA; } HTSprintf0 (&line, gettext("Making %s connection to %s."), protocol, host); @@ -1379,199 +1432,214 @@ FREE(host); FREE(line); - /* - ** Now, let's get a socket set up from the server for the data. - */ - *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (*s == -1) { - HTAlert(gettext("socket failed.")); - return HT_NO_DATA; - } + for (res = res0; res; res = res->ai_next) { + /* + ** Now, let's get a socket set up from the server for the data. + */ + *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, sizeof(hostbuf), portbuf, sizeof(portbuf), + NI_NUMERICHOST|NI_NUMERICSERV); + HTSprintf0 (&line, "socket failed: family %d addr %s port %s.", + res->ai_family, hostbuf, portbuf); + _HTProgress (line); + FREE(line); + continue; + } #ifndef DOSPATH #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. - */ - { + /* + ** 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); + int ret = fcntl(*s, F_SETFL, O_NONBLOCK); #else - int val = 1; - int ret = IOCTL(*s, FIONBIO, &val); + int val = 1; + int ret = IOCTL(*s, FIONBIO, &val); #endif /* USE_FCNTL */ - if (ret == -1) - _HTProgress(gettext("Could not make connection non-blocking.")); - } + if (ret == -1) + _HTProgress("Could not make connection non-blocking."); + } #endif /* !NO_IOCTL || USE_FCNTL */ #endif /* !DOSPATH */ - /* - ** 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) { - status = Rconnect(*s, (struct sockaddr*)&soc_address, - sizeof(soc_address)); /* - ** For long Rbind. + ** 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. */ - socks_bind_remoteAddr = soc_address.sin_addr.s_addr; - } else +#ifdef SOCKS + if (socks_flag) { + status = Rconnect(*s, res->ai_addr, res->ai_addrlen); + /* + ** For long Rbind. + */ + socks_bind_remoteAddr = soc_address.sin_addr.s_addr; + } else #endif /* SOCKS */ - status = connect(*s, (struct sockaddr*)&soc_address, sizeof(soc_address)); + status = connect(*s, res->ai_addr, res->ai_addrlen); #ifndef DJGPP - /* - ** According to the Sun man page for connect: - ** EINPROGRESS The socket is non-blocking and the con- - ** nection cannot be completed immediately. - ** It is possible to select(2) for comple- - ** tion by selecting the socket for writ- - ** ing. - ** According to the Motorola SVR4 man page for connect: - ** EAGAIN The socket is non-blocking and the con- - ** nection cannot be completed immediately. - ** It is possible to select for completion - ** by selecting the socket for writing. - ** However, this is only possible if the - ** socket STREAMS module is the topmost - ** module on the protocol stack with a - ** write service procedure. This will be - ** the normal case. - */ - if ((status < 0) && - (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EAGAIN)) { - struct timeval timeout; - int ret; - int tries=0; - - ret = 0; - while (ret <= 0) { - fd_set writefds; + /* + ** According to the Sun man page for connect: + ** EINPROGRESS The socket is non-blocking and the con- + ** nection cannot be completed immediately. + ** It is possible to select(2) for comple- + ** tion by selecting the socket for writ- + ** ing. + ** According to the Motorola SVR4 man page for connect: + ** EAGAIN The socket is non-blocking and the con- + ** nection cannot be completed immediately. + ** It is possible to select for completion + ** by selecting the socket for writing. + ** However, this is only possible if the + ** socket STREAMS module is the topmost + ** module on the protocol stack with a + ** write service procedure. This will be + ** the normal case. + */ + if ((status < 0) && + (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EAGAIN)) { + struct timeval timeout; + int ret; + int tries=0; + + ret = 0; + while (ret <= 0) { + fd_set writefds; - /* - ** Protect against an infinite loop. - */ - if (tries++ >= 180000) { - HTAlert(gettext("Connection failed for 180,000 tries.")); - return HT_NO_DATA; - } + /* + ** Protect against an infinite loop. + */ + if (tries++ >= 180000) { + HTAlert("Connection failed for 180,000 tries."); + FREE(line); + freeaddrinfo(res0); + return HT_NO_DATA; + } #ifdef _WINDOWS_NSL - timeout.tv_sec = 100; + timeout.tv_sec = 100; #else - timeout.tv_sec = 0; + timeout.tv_sec = 0; #endif /* _WINDOWS_NSL */ - timeout.tv_usec = 100000; - FD_ZERO(&writefds); - FD_SET(*s, &writefds); + timeout.tv_usec = 100000; + FD_ZERO(&writefds); + FD_SET(*s, &writefds); #ifdef SOCKS - if (socks_flag) - ret = Rselect(FD_SETSIZE, NULL, - (void *)&writefds, NULL, &timeout); - else + if (socks_flag) + ret = Rselect(FD_SETSIZE, NULL, + (void *)&writefds, NULL, &timeout); + else #endif /* SOCKS */ - ret = select(FD_SETSIZE, NULL, (void *)&writefds, NULL, &timeout); + ret = select(FD_SETSIZE, NULL, (void *)&writefds, NULL, &timeout); - /* - ** If we suspend, then it is possible that select will be - ** interrupted. Allow for this possibility. - JED - */ - if ((ret == -1) && (errno == EINTR)) - continue; + /* + ** If we suspend, then it is possible that select will be + ** interrupted. Allow for this possibility. - JED + */ + if ((ret == -1) && (errno == EINTR)) + continue; - /* - ** Again according to the Sun and Motorola man pages for connect: - ** EALREADY The socket is non-blocking and a previ- - ** ous connection attempt has not yet been - ** completed. - ** Thus if the SOCKET_ERRNO is NOT EALREADY we have a real error, - ** and should break out here and return that error. - ** Otherwise if it is EALREADY keep on trying to complete the - ** connection. - */ - if ((ret < 0) && (SOCKET_ERRNO != EALREADY)) { - status = ret; - break; - } else if (ret > 0) { /* - ** Extra check here for connection success, if we try to - ** connect again, and get EISCONN, it means we have a - ** successful connection. But don't check with SOCKS. + ** Again according to the Sun and Motorola man pages for connect: + ** EALREADY The socket is non-blocking and a previ- + ** ous connection attempt has not yet been + ** completed. + ** Thus if the SOCKET_ERRNO is NOT EALREADY we have a real error, + ** and should break out here and return that error. + ** Otherwise if it is EALREADY keep on trying to complete the + ** connection. */ + if ((ret < 0) && (SOCKET_ERRNO != EALREADY)) { + status = ret; + break; + } else if (ret > 0) { + /* + ** Extra check here for connection success, if we try to + ** connect again, and get EISCONN, it means we have a + ** successful connection. But don't check with SOCKS. + */ #ifdef SOCKS - if (socks_flag) { - status = 0; - } else { + if (socks_flag) { + status = 0; + } else #endif /* SOCKS */ - status = connect(*s, (struct sockaddr*)&soc_address, - sizeof(soc_address)); + { + status = connect(*s, res->ai_addr, res->ai_addrlen); #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))) + /* + ** 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)) + if ((status < 0) && (SOCKET_ERRNO == EISCONN)) #endif /* UCX */ - { - status = 0; - } + { + status = 0; + } - if (status && (SOCKET_ERRNO == EALREADY)) /* new stuff LJM */ - ret = 0; /* keep going */ - else - break; -#ifdef SOCKS + if (status && (SOCKET_ERRNO == EALREADY)) /* new stuff LJM */ + ret = 0; /* keep going */ + else + break; + } } -#endif /* SOCKS */ - } #ifdef SOCKS - else if (!socks_flag) + else if (!socks_flag) #else - 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 the EALREADY or EISCONN. - */ - status = connect(*s, (struct sockaddr*)&soc_address, - sizeof(soc_address)); - if ((status < 0) && - (SOCKET_ERRNO != EALREADY && SOCKET_ERRNO != EAGAIN) && -#ifdef UCX - (SOCKET_ERRNO != 18242) && -#endif /* UCX */ - (SOCKET_ERRNO != EISCONN)) { + { + /* + ** 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 the EALREADY or EISCONN. + */ + status = connect(*s, res->ai_addr, res->ai_addrlen); + if ((status < 0) && + (SOCKET_ERRNO != EALREADY && SOCKET_ERRNO != EAGAIN) && + #ifdef UCX + (SOCKET_ERRNO != 18242) && + #endif /* UCX */ + (SOCKET_ERRNO != EISCONN)) { + break; + } + } + if (HTCheckForInterrupt()) { + CTRACE(tfp, "*** INTERRUPTED in middle of connect.\n"); + status = HT_INTERRUPTED; + SOCKET_ERRNO = EINTR; break; } } - if (HTCheckForInterrupt()) { - CTRACE(tfp, "*** INTERRUPTED in middle of connect.\n"); - status = HT_INTERRUPTED; - SOCKET_ERRNO = EINTR; - break; - } } + + if (status < 0) { + NETCLOSE(*s); + *s = -1; + continue; + } + + break; } #endif /* !DJGPP */ - if (status < 0) { + if (*s < 0) { /* ** The connect attempt failed or was interrupted, ** so close up the socket. @@ -1596,6 +1664,8 @@ #endif /* !NO_IOCTL || USE_FCNTL */ #endif /* !DOSPATH */ + FREE(line); + freeaddrinfo(res0); return status; }