1 #include "syshdrs.h"
2 
3 #ifndef NO_SIGNALS
4 extern volatile Sjmp_buf gNetTimeoutJmp;
5 extern volatile Sjmp_buf gPipeJmp;
6 #endif
7 
8 int
9 SConnect(int sfd, const struct sockaddr_in *const addr, int tlen)
10 {
11 #ifndef NO_SIGNALS
12 	int result;
13 	vsio_sigproc_t sigalrm;
14 
15 	if (SSetjmp(gNetTimeoutJmp) != 0) {
16 		alarm(0);
17 		(void) SSignal(SIGALRM, (sio_sigproc_t) sigalrm);
18 		errno = ETIMEDOUT;
19 		return (kTimeoutErr);
20 	}
21 
22 	sigalrm = (vsio_sigproc_t) SSignal(SIGALRM, SIOHandler);
23 	alarm((unsigned int) tlen);
24 
25 	errno = 0;
26 	do {
27 		result = connect(sfd, (struct sockaddr *) addr,
28 			(int) sizeof(struct sockaddr_in));
29 	} while ((result < 0) && (errno == EINTR));
30 
31 	alarm(0);
32 	(void) SSignal(SIGALRM, (sio_sigproc_t) sigalrm);
33 	return (result);
34 #else	/* NO_SIGNALS */
35 	unsigned long opt;
36 	fd_set ss, xx;
37 	struct timeval tv;
38 	int result;
39 	int cErrno;
40 #if defined(WIN32) || defined(_WINDOWS)
41 	int wsaErrno;
42 	int soerr, soerrsize;
43 #else
44 	int optval;
45 	int optlen;
46 #endif
47 
48 	errno = 0;
49 	if (tlen <= 0) {
50 		do {
51 			result = connect(sfd, (struct sockaddr *) addr,
52 				(int) sizeof(struct sockaddr_in));
53 			SETERRNO
54 		} while ((result < 0) && (errno == EINTR));
55 		return (result);
56 	}
57 
58 #ifdef FIONBIO
59 	opt = 1;
60 	if (ioctlsocket(sfd, FIONBIO, &opt) != 0) {
61 		SETERRNO
62 		return (-1);
63 	}
64 #else
65 	if (fcntl(sfd, F_GETFL, &opt) < 0) {
66 		SETERRNO
67 		return (-1);
68 	} else if (fcntl(sfd, F_SETFL, opt | O_NONBLOCK) < 0) {
69 		SETERRNO
70 		return (-1);
71 	}
72 #endif
73 
74 	errno = 0;
75 	result = connect(sfd, (struct sockaddr *) addr,
76 			(int) sizeof(struct sockaddr_in));
77 	if (result == 0)
78 		return 0;	/* Already?!? */
79 
80 	if ((result < 0)
81 #if defined(WIN32) || defined(_WINDOWS)
82 		&& ((wsaErrno = WSAGetLastError()) != WSAEWOULDBLOCK)
83 		&& (wsaErrno != WSAEINPROGRESS)
84 #else
85 		&& (errno != EWOULDBLOCK) && (errno != EINPROGRESS)
86 #endif
87 		) {
88 		SETERRNO
89 		shutdown(sfd, 2);
90 		return (-1);
91 	}
92 	cErrno = errno;
93 
94 	forever {
95 #if defined(WIN32) || defined(_WINDOWS)
96 		WSASetLastError(0);
97 #endif
98 		FD_ZERO(&ss);
99 		FD_SET(sfd, &ss);
100 		xx = ss;
101 		tv.tv_sec = tlen;
102 		tv.tv_usec = 0;
103 		result = select(sfd + 1, NULL, SELECT_TYPE_ARG234 &ss, SELECT_TYPE_ARG234 &xx, SELECT_TYPE_ARG5 &tv);
104 		if (result == 1) {
105 			/* ready */
106 			break;
107 		} else if (result == 0) {
108 			/* timeout */
109 			errno = ETIMEDOUT;
110 			SETWSATIMEOUTERR
111 			/* Don't bother turning off FIONBIO */
112 			return (kTimeoutErr);
113 		} else if (errno != EINTR) {
114 			/* Don't bother turning off FIONBIO */
115 			SETERRNO
116 			return (-1);
117 		}
118 	}
119 
120 	/* Supposedly once the select() returns with a writable
121 	 * descriptor, it is connected and we don't need to
122 	 * recall connect().  When select() returns an exception,
123 	 * the connection failed -- we can get the connect error
124 	 * doing a write on the socket which will err out.
125 	 */
126 
127 	if (FD_ISSET(sfd, &xx)) {
128 #if defined(WIN32) || defined(_WINDOWS)
129 		errno = 0;
130 		soerr = 0;
131 		soerrsize = sizeof(soerr);
132 		result = getsockopt(sfd, SOL_SOCKET, SO_ERROR, (char *) &soerr, &soerrsize);
133 		if ((result >= 0) && (soerr != 0)) {
134 			errno = soerr;
135 		} else {
136 			errno = 0;
137 			(void) send(sfd, "\0", 1, 0);
138 			SETERRNO
139 		}
140 #else
141 		errno = 0;
142 		(void) send(sfd, "\0", 1, 0);
143 #endif
144 		result = errno;
145 		shutdown(sfd, 2);
146 		errno = result;
147 		return (-1);
148 	}
149 
150 #if defined(WIN32) || defined(_WINDOWS)
151 #else
152 	if (cErrno == EINPROGRESS) {
153 		/*
154 		 * [from Linux connect(2) page]
155 		 *
156 		 * EINPROGRESS
157 		 *
158 		 * The socket is non-blocking and the connection can�
159 		 * not  be  completed immediately.  It is possible to
160 		 * select(2) or poll(2) for completion  by  selecting
161 		 * the  socket  for  writing.  After select indicates
162 		 * writability,  use  getsockopt(2)   to   read   the
163 		 * SO_ERROR  option  at level SOL_SOCKET to determine
164 		 * whether connect completed  successfully  (SO_ERROR
165 		 * is zero) or unsuccessfully (SO_ERROR is one of the
166 		 * usual error codes  listed  above,  explaining  the
167 		 * reason for the failure).
168 	         */
169 		optval = 0;
170 		optlen = sizeof(optval);
171 		if (getsockopt(sfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == 0) {
172 			errno = optval;
173 			if (errno != 0)
174 				return (-1);
175 		}
176 	}
177 #endif
178 
179 #ifdef FIONBIO
180 	opt = 0;
181 	if (ioctlsocket(sfd, FIONBIO, &opt) != 0) {
182 		SETERRNO
183 		shutdown(sfd, 2);
184 		return (-1);
185 	}
186 #else
187 	if (fcntl(sfd, F_SETFL, opt) < 0) {
188 		SETERRNO
189 		shutdown(sfd, 2);
190 		return (-1);
191 	}
192 #endif
193 
194 	return (0);
195 #endif	/* NO_SIGNALS */
196 }	/* SConnect */
197