#include "syshdrs.h" #ifndef NO_SIGNALS extern volatile Sjmp_buf gNetTimeoutJmp; extern volatile Sjmp_buf gPipeJmp; #endif int SConnect(int sfd, const struct sockaddr_in *const addr, int tlen) { #ifndef NO_SIGNALS int result; vsio_sigproc_t sigalrm; if (SSetjmp(gNetTimeoutJmp) != 0) { alarm(0); (void) SSignal(SIGALRM, (sio_sigproc_t) sigalrm); errno = ETIMEDOUT; return (kTimeoutErr); } sigalrm = (vsio_sigproc_t) SSignal(SIGALRM, SIOHandler); alarm((unsigned int) tlen); errno = 0; do { result = connect(sfd, (struct sockaddr *) addr, (int) sizeof(struct sockaddr_in)); } while ((result < 0) && (errno == EINTR)); alarm(0); (void) SSignal(SIGALRM, (sio_sigproc_t) sigalrm); return (result); #else /* NO_SIGNALS */ unsigned long opt; fd_set ss, xx; struct timeval tv; int result; int cErrno; #if defined(WIN32) || defined(_WINDOWS) int wsaErrno; int soerr, soerrsize; #else int optval; int optlen; #endif errno = 0; if (tlen <= 0) { do { result = connect(sfd, (struct sockaddr *) addr, (int) sizeof(struct sockaddr_in)); SETERRNO } while ((result < 0) && (errno == EINTR)); return (result); } #ifdef FIONBIO opt = 1; if (ioctlsocket(sfd, FIONBIO, &opt) != 0) { SETERRNO return (-1); } #else if (fcntl(sfd, F_GETFL, &opt) < 0) { SETERRNO return (-1); } else if (fcntl(sfd, F_SETFL, opt | O_NONBLOCK) < 0) { SETERRNO return (-1); } #endif errno = 0; result = connect(sfd, (struct sockaddr *) addr, (int) sizeof(struct sockaddr_in)); if (result == 0) return 0; /* Already?!? */ if ((result < 0) #if defined(WIN32) || defined(_WINDOWS) && ((wsaErrno = WSAGetLastError()) != WSAEWOULDBLOCK) && (wsaErrno != WSAEINPROGRESS) #else && (errno != EWOULDBLOCK) && (errno != EINPROGRESS) #endif ) { SETERRNO shutdown(sfd, 2); return (-1); } cErrno = errno; forever { #if defined(WIN32) || defined(_WINDOWS) WSASetLastError(0); #endif FD_ZERO(&ss); FD_SET(sfd, &ss); xx = ss; tv.tv_sec = tlen; tv.tv_usec = 0; result = select(sfd + 1, NULL, SELECT_TYPE_ARG234 &ss, SELECT_TYPE_ARG234 &xx, SELECT_TYPE_ARG5 &tv); if (result == 1) { /* ready */ break; } else if (result == 0) { /* timeout */ errno = ETIMEDOUT; SETWSATIMEOUTERR /* Don't bother turning off FIONBIO */ return (kTimeoutErr); } else if (errno != EINTR) { /* Don't bother turning off FIONBIO */ SETERRNO return (-1); } } /* Supposedly once the select() returns with a writable * descriptor, it is connected and we don't need to * recall connect(). When select() returns an exception, * the connection failed -- we can get the connect error * doing a write on the socket which will err out. */ if (FD_ISSET(sfd, &xx)) { #if defined(WIN32) || defined(_WINDOWS) errno = 0; soerr = 0; soerrsize = sizeof(soerr); result = getsockopt(sfd, SOL_SOCKET, SO_ERROR, (char *) &soerr, &soerrsize); if ((result >= 0) && (soerr != 0)) { errno = soerr; } else { errno = 0; (void) send(sfd, "\0", 1, 0); SETERRNO } #else errno = 0; (void) send(sfd, "\0", 1, 0); #endif result = errno; shutdown(sfd, 2); errno = result; return (-1); } #if defined(WIN32) || defined(_WINDOWS) #else if (cErrno == EINPROGRESS) { /* * [from Linux connect(2) page] * * EINPROGRESS * * The socket is non-blocking and the connection can­ * not be completed immediately. It is possible to * select(2) or poll(2) for completion by selecting * the socket for writing. After select indicates * writability, use getsockopt(2) to read the * SO_ERROR option at level SOL_SOCKET to determine * whether connect completed successfully (SO_ERROR * is zero) or unsuccessfully (SO_ERROR is one of the * usual error codes listed above, explaining the * reason for the failure). */ optval = 0; optlen = sizeof(optval); if (getsockopt(sfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == 0) { errno = optval; if (errno != 0) return (-1); } } #endif #ifdef FIONBIO opt = 0; if (ioctlsocket(sfd, FIONBIO, &opt) != 0) { SETERRNO shutdown(sfd, 2); return (-1); } #else if (fcntl(sfd, F_SETFL, opt) < 0) { SETERRNO shutdown(sfd, 2); return (-1); } #endif return (0); #endif /* NO_SIGNALS */ } /* SConnect */