📄 httcp.c
字号:
for (res = res0; res; res = res->ai_next) { *s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (*s == -1) { char hostbuf[1024], portbuf[1024]; getnameinfo(res->ai_addr, res->ai_addrlen, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST|NI_NUMERICSERV); HTSprintf0 (&line, gettext("socket failed: family %d addr %s port %s."), res->ai_family, hostbuf, portbuf); _HTProgress (line); FREE(line); continue; }#else *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (*s == -1) { HTAlert(gettext("socket failed.")); return HT_NO_DATA; }#endif /* INET6 */#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. */ {#ifdef USE_FCNTL int ret = fcntl(*s, F_SETFL, O_NONBLOCK);#else int val = 1; int ret = IOCTL(*s, FIONBIO, &val);#endif /* USE_FCNTL */ if (ret == -1) _HTProgress(gettext("Could not make connection non-blocking.")); }#endif /* !NO_IOCTL || USE_FCNTL */#endif /* !DOSPATH */ /* ** Issue the connect. Since the server can't do an instantaneous ** accept and we are non-blocking, this will almost certainly return ** a negative status. */#ifdef SOCKS if (socks_flag) {#ifdef INET6 status = Rconnect(*s, res->ai_addr, res->ai_addrlen);#else status = Rconnect(*s, (struct sockaddr*)&soc_address, sizeof(soc_address));#endif /* INET6 */ /* ** For long Rbind. */ socks_bind_remoteAddr = soc_address.sin_addr.s_addr; } else#endif /* SOCKS */#ifdef INET6 status = connect(*s, res->ai_addr, res->ai_addrlen);#else status = connect(*s, (struct sockaddr*)&soc_address, sizeof(soc_address));#endif /* INET6 */#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#ifdef EAGAIN || SOCKET_ERRNO == EAGAIN#endif )) { struct timeval select_timeout; int ret; int tries=0;#ifdef SOCKET_DEBUG_TRACE HTInetStatus("this socket's first connect");#endif /* SOCKET_DEBUG_TRACE */ ret = 0; while (ret <= 0) { fd_set writefds; /* ** Protect against an infinite loop. */ if ((tries++/10) >= connect_timeout) { HTAlert(gettext("Connection failed (too many retries)."));#ifdef INET6 FREE(line); freeaddrinfo(res0);#endif /* INET6 */ return HT_NO_DATA; }#ifdef _WINDOWS_NSL select_timeout.tv_sec = connect_timeout; select_timeout.tv_usec = 0;#else select_timeout.tv_sec = 0; select_timeout.tv_usec = 100000;#endif /* _WINDOWS_NSL */ FD_ZERO(&writefds); FD_SET((unsigned) *s, &writefds);#ifdef SOCKS if (socks_flag) ret = Rselect(FD_SETSIZE, NULL, (void *)&writefds, NULL, &select_timeout); else#endif /* SOCKS */ ret = select(FD_SETSIZE, NULL, (void *)&writefds, NULL, &select_timeout);#ifdef SOCKET_DEBUG_TRACE if (tries == 1) { HTInetStatus("this socket's first select"); }#endif /* SOCKET_DEBUG_TRACE */ /* ** If we suspend, then it is possible that select will be ** interrupted. Allow for this possibility. - JED */ if ((ret == -1) && (errno == EINTR)) continue;#ifdef SOCKET_DEBUG_TRACE if (ret < 0) { HTInetStatus("failed select"); }#endif /* SOCKET_DEBUG_TRACE */ /* ** Again according to the Sun and Motorola man pages for connect: ** EALREADY The socket is non-blocking and a previ- ** ous connection attempt has not yet been ** completed. ** Thus if the SOCKET_ERRNO is NOT EALREADY we have a real error, ** and should break out here and return that error. ** Otherwise if it is EALREADY keep on trying to complete the ** connection. */ if ((ret < 0) && (SOCKET_ERRNO != EALREADY)) { status = ret; break; } else if (ret > 0) { /* ** Extra check here for connection success, if we try to ** connect again, and get EISCONN, it means we have a ** successful connection. But don't check with SOCKS. */#ifdef SOCKS if (socks_flag) { status = 0; } else {#endif /* SOCKS */#ifdef INET6 status = connect(*s, res->ai_addr, res->ai_addrlen);#else status = connect(*s, (struct sockaddr*)&soc_address, sizeof(soc_address));#endif /* INET6 */#ifdef UCX /* ** A UCX feature: Instead of returning EISCONN ** UCX returns EADDRINUSE. ** Test for this status also. */ if ((status < 0) && ((SOCKET_ERRNO == EISCONN) || (SOCKET_ERRNO == EADDRINUSE)))#else if ((status < 0) && (SOCKET_ERRNO == EISCONN))#endif /* UCX */ { status = 0; } if (status && (SOCKET_ERRNO == EALREADY)) /* new stuff LJM */ ret = 0; /* keep going */ else {#ifdef SOCKET_DEBUG_TRACE if (status < 0) { HTInetStatus("confirm-ready connect"); }#endif /* SOCKET_DEBUG_TRACE */ break; }#ifdef SOCKS }#endif /* SOCKS */ }#ifdef SOCKS else if (!socks_flag)#else else#endif /* SOCKS */ { /* ** The select says we aren't ready yet. Try to connect ** again to make sure. If we don't get EALREADY or EISCONN, ** something has gone wrong. Break out and report it. ** ** For some reason, SVR4 returns EAGAIN here instead of ** EALREADY, even though the man page says it should be ** EALREADY. ** ** For some reason, UCX pre 3 apparently returns ** errno = 18242 instead the EALREADY or EISCONN. */#ifdef INET6 status = connect(*s, res->ai_addr, res->ai_addrlen);#else status = connect(*s, (struct sockaddr*)&soc_address, sizeof(soc_address));#endif /* INET6 */ if ((status < 0) && (SOCKET_ERRNO != EALREADY#ifdef EAGAIN && SOCKET_ERRNO != EAGAIN#endif ) &&#ifdef UCX (SOCKET_ERRNO != 18242) &&#endif /* UCX */ (SOCKET_ERRNO != EISCONN)) {#ifdef SOCKET_DEBUG_TRACE HTInetStatus("confirm-not-ready connect");#endif /* SOCKET_DEBUG_TRACE */ break; } } if (HTCheckForInterrupt()) { CTRACE((tfp, "*** INTERRUPTED in middle of connect.\n")); status = HT_INTERRUPTED;#ifdef _WINDOWS WSASetLastError(EINTR);#else SOCKET_ERRNO = EINTR;#endif break; } } }#ifdef SOCKET_DEBUG_TRACE else if (status < 0) { HTInetStatus("this socket's first and only connect"); }#endif /* SOCKET_DEBUG_TRACE */#ifdef INET6 if (status < 0) { NETCLOSE(*s); *s = -1; continue; } break; }#endif /* INET6 */#endif /* !__DJGPP__ */#ifdef INET6 if (*s < 0)#else if (status < 0)#endif /* INET6 */ { /* ** The connect attempt failed or was interrupted, ** so close up the socket. */ NETCLOSE(*s); }#ifndef DOSPATH#if !defined(NO_IOCTL) || defined(USE_FCNTL) else { /* ** Make the socket blocking again on good connect. */#ifdef USE_FCNTL int ret = fcntl(*s, F_SETFL, 0);#else int val = 0; int ret = IOCTL(*s, FIONBIO, &val);#endif /* USE_FCNTL */ if (ret == -1) _HTProgress(gettext("Could not restore socket to blocking.")); }#endif /* !NO_IOCTL || USE_FCNTL */#endif /* !DOSPATH */#ifdef INET6 FREE(line); freeaddrinfo(res0);#endif /* INET6 */ return status;}/*** This is so interruptible reads can be implemented cleanly.*/PUBLIC int HTDoRead ARGS3( int, fildes, void *, buf, unsigned, nbyte){ int ready, ret; fd_set readfds; struct timeval select_timeout; int tries=0;#ifdef EXP_READPROGRESS int otries = 0; time_t otime = time((time_t *)0);#endif#if defined(UNIX) || defined(UCX) int nb;#endif /* UCX, BSN */#ifdef UNIX if (fildes == 0) { /* * 0 can be a valid socket fd, but if it's a tty something must * have gone wrong. - kw */ if (isatty(fildes)) { CTRACE((tfp, "HTDoRead - refusing to read fd 0 which is a tty!\n")); return -1; } } else#endif if (fildes <= 0) return -1; if (HTCheckForInterrupt()) {#ifdef _WINDOWS WSASetLastError(EINTR);#else SOCKET_ERRNO = EINTR;#endif return (HT_INTERRUPTED); }#if !defined(NO_IOCTL) ready = 0;#else ready = 1;#endif /* bypass for NO_IOCTL */ while (!ready) { /* ** Protect against an infinite loop. */ if (tries++ >= 180000) { HTAlert(gettext("Socket read failed for 180,000 tries."));#ifdef _WINDOWS WSASetLastError(EINTR);#else SOCKET_ERRNO = EINTR;#endif return HT_INTERRUPTED; }#ifdef EXP_READPROGRESS if (tries - otries > 10) { time_t t = time((time_t *)0); otries = tries; if (t - otime >= 5) { otime = t; HTReadProgress(-1, 0); /* Put "stalled" message */ } }#endif /* ** If we suspend, then it is possible that select will be ** interrupted. Allow for this possibility. - JED */ do { select_timeout.tv_sec = 0; select_timeout.tv_usec = 100000; FD_ZERO(&readfds); FD_SET((unsigned)fildes, &readfds);#ifdef SOCKS if (socks_flag) ret = Rselect(FD_SETSIZE, (void *)&readfds, NULL, NULL, &select_timeout); else#endif /* SOCKS */ ret = select(FD_SETSIZE, (void *)&readfds, NULL, NULL, &select_timeout); } while ((ret == -1) && (errno == EINTR)); if (ret < 0) { return -1; } else if (ret > 0) { ready = 1; } else if (HTCheckForInterrupt()) {#ifdef _WINDOWS WSASetLastError(EINTR);#else SOCKET_ERRNO = EINTR;#endif return HT_INTERRUPTED; } }#if !defined(UCX) || !defined(VAXC)#ifdef UNIX while ((nb = SOCKET_READ (fildes, buf, nbyte)) == -1) { if (errno == EINTR) continue;#ifdef ERESTARTSYS if (errno == ERESTARTSYS) continue;#endif /* ERESTARTSYS */ HTInetStatus("read"); break; } return nb;#else /* UNIX */ return SOCKET_READ (fildes, buf, nbyte);#endif /* !UNIX */#else /* UCX && VAXC */ /* ** VAXC and UCX problem only. */ errno = vaxc$errno = 0; nb = SOCKET_READ (fildes, buf, nbyte); CTRACE((tfp, "Read - nb,errno,vaxc$errno: %d %d %d\n", nb,errno,vaxc$errno)); if ((nb <= 0) && TRACE) perror ("HTTCP.C:HTDoRead:read"); /* RJF */ /* ** An errno value of EPIPE and nb < 0 indicates end-of-file on VAXC. */ if ((nb <= 0) && (errno == EPIPE)) { nb = 0; set_errno(0); } return nb;#endif /* UCX, BSN */}#ifdef SVR4_BSDSELECT/*** This is a fix for the difference between BSD's select() and** SVR4's select(). SVR4's select() can never return a value larger** than the total number of file descriptors being checked. So, if** you select for read and write on one file descriptor, and both** are true, SVR4 select() will only return 1. BSD select in the** same situation will return 2.**** Additionally, BSD select() on timing out, will zero the masks,** while SVR4 does not. This is fixed here as well.**** Set your tabstops to 4 characters to have this code nicely formatted.**** Jerry Whelan, guru@bradley.edu, June 12th, 1993*/#ifdef select#undef select#endif /* select */#ifdef SOCKS#ifdef Rselect#undef Rselect#endif /* Rselect */#endif /* SOCKS */#include <sys/types.h>#include <sys/time.h>#include <sys/select.h>PUBLIC int BSDselect ARGS5( int, nfds, fd_set *, readfds, fd_set *, writefds, fd_set *, exceptfds, struct timeval *, select_timeout){ int rval, i;#ifdef SOCKS if (socks_flag) rval = Rselect(nfds, readfds, writefds, exceptfds, select_timeout); else#endif /* SOCKS */ rval = select(nfds, readfds, writefds, exceptfds, select_timeout); switch (rval) { case -1: return(rval); case 0: if (readfds != NULL) FD_ZERO(readfds); if (writefds != NULL) FD_ZERO(writefds); if (exceptfds != NULL) FD_ZERO(exceptfds); return(rval); default: for (i = 0, rval = 0; i < nfds; i++) { if ((readfds != NULL) && FD_ISSET(i, readfds)) rval++; if ((writefds != NULL) && FD_ISSET(i, writefds)) rval++; if ((exceptfds != NULL) && FD_ISSET(i, exceptfds)) rval++; } return(rval); }/* Should never get here */}#endif /* SVR4_BSDSELECT */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -