reactos/modules/rosapps/applications/net/ncftp/sio/SConnect.c

197 lines
4.2 KiB
C
Raw Blame History

#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<61>
* 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 */