Watt-32 tcp/ip  2.2 dev-rel.10
connect.c
Go to the documentation of this file.
1 
5 /* BSD sockets functionality for Watt-32 TCP/IP
6  *
7  * Copyright (c) 1997-2002 Gisle Vanem <gvanem@yahoo.no>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  * must display the following acknowledgement:
19  * This product includes software developed by Gisle Vanem
20  * Bergen, Norway.
21  *
22  * THIS SOFTWARE IS PROVIDED BY ME (Gisle Vanem) AND CONTRIBUTORS ``AS IS''
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL I OR CONTRIBUTORS BE LIABLE FOR ANY
26  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Version
34  *
35  * 0.5 : Dec 18, 1997 : G. Vanem - created
36  * 0.6 : Aug 06, 2002 : G. Vanem - added AF_INET6 support
37  */
38 
39 #include "socket.h"
40 
41 #if defined(USE_BSD_API)
42 
43 static int tcp_connect (Socket *socket);
44 static int udp_connect (Socket *socket);
45 static int raw_connect (Socket *socket);
46 static int nblk_connect (Socket *socket);
47 
48 /*
49  * connect()
50  * "connect" will attempt to open a connection on a foreign IP address and
51  * foreign port address. This is achieved by specifying the foreign IP
52  * address and foreign port number in the "servaddr".
53  */
54 int W32_CALL connect (int s, const struct sockaddr *servaddr, int addrlen)
55 {
56  struct sockaddr_in *addr = (struct sockaddr_in*) servaddr;
57  struct sockaddr_in6 *addr6 = (struct sockaddr_in6*) servaddr;
58  struct Socket *socket = _socklist_find (s);
59  volatile int rc, sa_len;
60  BOOL is_ip6;
61 
62  SOCK_PROLOGUE (socket, "\nconnect:%d", s);
63 
64  is_ip6 = (socket->so_family == AF_INET6);
65  sa_len = is_ip6 ? sizeof(*addr6) : sizeof(*addr);
66 
67  if (_sock_chk_sockaddr(socket, servaddr, addrlen) < 0)
68  return (-1);
69 
70  if (socket->so_type == SOCK_STREAM)
71  {
72  if (socket->so_state & SS_ISCONNECTED)
73  {
74  SOCK_DEBUGF ((", EISCONN"));
75  SOCK_ERRNO (EISCONN);
76  return (-1);
77  }
78  if (socket->so_options & SO_ACCEPTCONN)
79  {
80  SOCK_DEBUGF ((", EOPNOTSUPP (listen sock)"));
81  SOCK_ERRNO (EOPNOTSUPP);
82  return (-1);
83  }
84  if (!is_ip6 && IN_MULTICAST(ntohl(addr->sin_addr.s_addr)))
85  {
86  SOCK_DEBUGF ((", EINVAL (mcast)"));
87  SOCK_ERRNO (EINVAL);
88  return (-1);
89  }
90  else if (is_ip6 && IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr))
91  {
92  SOCK_DEBUGF ((", EINVAL (mcast)"));
93  SOCK_ERRNO (EINVAL);
94  return (-1);
95  }
96  }
97 
98  if (socket->remote_addr)
99  {
100  if ((socket->so_type == SOCK_STREAM) &&
101  (socket->so_state & SS_NBIO))
102  return nblk_connect (socket);
103 
104  SOCK_DEBUGF ((", connect already done!"));
105  SOCK_ERRNO (EISCONN);
106  return (-1);
107  }
108 
109  socket->remote_addr = (struct sockaddr_in*) SOCK_CALLOC (sa_len);
110  if (!socket->remote_addr)
111  {
112  SOCK_DEBUGF ((", ENOMEM (rem)"));
113  SOCK_ERRNO (ENOMEM);
114  return (-1);
115  }
116 
117 #if defined(USE_IPV6)
118  if (is_ip6)
119  {
120  struct sockaddr_in6 *ra = (struct sockaddr_in6*)socket->remote_addr;
121 
122  ra->sin6_family = AF_INET6;
123  ra->sin6_port = addr6->sin6_port;
124  memcpy (&ra->sin6_addr, &addr6->sin6_addr, sizeof(ra->sin6_addr));
125  }
126  else
127 #endif
128  {
129  socket->remote_addr->sin_family = AF_INET;
130  socket->remote_addr->sin_port = addr->sin_port;
131  socket->remote_addr->sin_addr = addr->sin_addr;
132  }
133 
134  if (!socket->local_addr)
135  {
136  SOCK_DEBUGF ((", auto-binding"));
137 
138  socket->local_addr = (struct sockaddr_in*) SOCK_CALLOC (sa_len);
139  if (!socket->local_addr)
140  {
141  free (socket->remote_addr);
142  socket->remote_addr = NULL;
143  SOCK_DEBUGF ((", ENOMEM (loc)"));
144  SOCK_ERRNO (ENOMEM);
145  return (-1);
146  }
147 
148 #if defined(USE_IPV6)
149  if (is_ip6)
150  {
151  struct sockaddr_in6 *la = (struct sockaddr_in6*)socket->local_addr;
152 
153  la->sin6_family = AF_INET6;
154  la->sin6_port = htons (find_free_port(0,TRUE));
155  memcpy (&la->sin6_addr, &in6addr_my_ip, sizeof(la->sin6_addr));
156  }
157  else
158 #endif
159  {
160  socket->local_addr->sin_family = AF_INET;
161  socket->local_addr->sin_port = htons (find_free_port(0,TRUE));
162  socket->local_addr->sin_addr.s_addr = htonl (my_ip_addr);
163  }
164  }
165 
166  SOCK_DEBUGF ((", src/dest ports: %u/%u",
167  ntohs(socket->local_addr->sin_port),
168  ntohs(socket->remote_addr->sin_port)));
169 
170 
171  /* Not safe to run sock_daemon() now
172  */
174 
175  /* Setup SIGINT handler now.
176  */
177  if (_sock_sig_setup() < 0)
178  {
179  SOCK_DEBUGF ((", EINTR"));
180  SOCK_ERRNO (EINTR);
181  _sock_crit_stop();
182  return (-1);
183  }
184 
185  switch (socket->so_type)
186  {
187  case SOCK_STREAM:
188  rc = tcp_connect (socket);
189  break;
190 
191  case SOCK_DGRAM:
192  rc = udp_connect (socket);
193  break;
194 
195  case SOCK_RAW:
196  rc = raw_connect (socket);
197  break;
198 
199  default:
200  SOCK_ERRNO (EPROTONOSUPPORT);
201  rc = -1;
202  break;
203  }
204 
205  _sock_sig_restore();
206  _sock_crit_stop();
207  return (rc);
208 }
209 
210 /*
211  * Handle SOCK_DGRAM connection. Always blocking in _arp_resolve()
212  */
213 static int udp_connect (Socket *socket)
214 {
215 #if defined(USE_IPV6)
216  if (socket->so_family == AF_INET6)
217  {
218  const struct sockaddr_in6 *la = (const struct sockaddr_in6*) socket->local_addr;
219  const struct sockaddr_in6 *ra = (const struct sockaddr_in6*) socket->remote_addr;
220 
221  if (!_UDP6_open (socket, &ra->sin6_addr, la->sin6_port, ra->sin6_port))
222  {
223  SOCK_DEBUGF ((", no route (udp6)"));
224  SOCK_ERRNO (EHOSTUNREACH);
225  STAT (ip6stats.ip6s_noroute++);
226  return (-1);
227  }
228  }
229  else
230 #endif
231  if (!_UDP_open (socket,
232  socket->remote_addr->sin_addr,
233  socket->local_addr->sin_port,
234  socket->remote_addr->sin_port))
235  {
236  /* errno already set in udp_open() */
237  SOCK_DEBUGF ((", %s", socket->udp_sock->err_msg));
238  return (-1);
239  }
240 
241  if ((socket->so_state & SS_PRIV) && socket->bcast_pool)
242  {
243  /* undo what udp_open() did above.
244  * !!Fix me: clears recv data.
245  */
246  sock_recv_init ((sock_type*)socket->udp_sock,
247  socket->bcast_pool, socket->pool_size);
248  }
249 
250  socket->so_state &= ~SS_UNCONNECTED;
251  socket->so_state |= SS_ISCONNECTED;
252  return (0);
253 }
254 
255 
256 /*
257  * SOCK_RAW "connect" is very simple.
258  */
259 static int raw_connect (Socket *socket)
260 {
261  socket->so_state |= SS_ISCONNECTED;
262 
263  /* Note: _arp_resolve() is done in ip_transmit()
264  */
265  return (0);
266 }
267 
268 /*
269  * Check for catched signals
270  */
271 static int chk_signals (void)
272 {
273  if (_sock_sig_pending())
274  {
275  SOCK_DEBUGF ((", EINTR"));
276  SOCK_ERRNO (EINTR);
277  return (-1);
278  }
279  return (0);
280 }
281 
282 /*
283  * Handle SOCK_STREAM blocking connection. Or non-blocking connect
284  * the first time connect() is called.
285  */
286 static int tcp_connect (Socket *socket)
287 {
288  DWORD timeout;
289  int status;
290 
291 #if defined(USE_IPV6)
292  if (socket->so_family == AF_INET6)
293  {
294  const struct sockaddr_in6 *la = (const struct sockaddr_in6*) socket->local_addr;
295  const struct sockaddr_in6 *ra = (const struct sockaddr_in6*) socket->remote_addr;
296 
297  if (!_TCP6_open (socket, &ra->sin6_addr, la->sin6_port, ra->sin6_port))
298  {
299  /* errno already set in _TCP6_open() */
300  SOCK_DEBUGF ((", %s", socket->tcp_sock->err_msg));
301  return (-1);
302  }
303  }
304  else
305 #endif
306  if (!_TCP_open (socket,
307  socket->remote_addr->sin_addr,
308  socket->local_addr->sin_port,
309  socket->remote_addr->sin_port))
310  {
311  /* errno already set in tcp_open() */
312  SOCK_DEBUGF ((", %s", socket->tcp_sock->err_msg));
313  return (-1);
314  }
315 
316  /* Don't let tcp_Retransmitter() kill this socket
317  * before our `socket->timeout' expires
318  */
319  socket->tcp_sock->locflags |= LF_RCVTIMEO;
320 
321  /* We're here only when connect() is called the 1st time
322  * (blocking or non-blocking socket).
323  */
324  socket->so_state |= SS_ISCONNECTING;
325  timeout = set_timeout (1000 * socket->timeout);
326 
327  if (socket->so_state & SS_NBIO)
328  {
329  /* if user calls getsockopt(SO_ERROR) before calling connect() again
330  */
331  socket->so_error = EALREADY;
332  socket->nb_timer = timeout;
333  SOCK_DEBUGF ((", EINPROGRESS"));
334  SOCK_ERRNO (EINPROGRESS);
335  return (-1);
336  }
337 
338  /* Handle blocking stream socket connect.
339  * Maybe we should use select_s() instead ?
340  * Maybe set LF_NOCLOSE for all BSD sockets?
341  */
342  status = _ip_delay0 ((sock_type*)socket->tcp_sock,
343  socket->timeout, (UserHandler)chk_signals,
344  NULL);
345 
346  if (socket->so_error == EHOSTUNREACH)
347  {
348  SOCK_DEBUGF ((", %s", socket->tcp_sock->err_msg ?
349  socket->tcp_sock->err_msg : "no route"));
350  SOCK_ERRNO (EHOSTUNREACH);
351  return (-1);
352  }
353 
354  /* We got an ICMP_UNREACH from someone
355  */
356  if (socket->so_state & SS_CONN_REFUSED)
357  {
358  socket->so_state &= ~SS_ISCONNECTING;
359  SOCK_DEBUGF ((", ECONNREFUSED"));
360  SOCK_ERRNO (ECONNREFUSED);
361  return (-1);
362  }
363 
364  if (status < 0 && chk_timeout(timeout))
365  {
366  socket->so_state &= ~SS_ISCONNECTING;
367  SOCK_DEBUGF ((", ETIMEDOUT"));
368  SOCK_ERRNO (ETIMEDOUT);
369  return (-1);
370  }
371 
372  if (status < 0)
373  {
374  socket->so_state &= ~SS_ISCONNECTING;
375  SOCK_DEBUGF ((", ECONNRESET"));
376  SOCK_ERRNO (ECONNRESET);
377  return (-1);
378  }
379 
380  socket->so_state &= ~(SS_UNCONNECTED | SS_ISCONNECTING);
381  socket->so_state |= SS_ISCONNECTED;
382  return (0);
383 }
384 
385 
386 /*
387  * Handle non-blocking SOCK_STREAM connection.
388  * Only called on 2nd (3rd etc) time a non-blocking
389  * connect() is called.
390  */
391 static int nblk_connect (Socket *socket)
392 {
393  if (socket->so_state & SS_ISCONNECTED)
394  {
395  SOCK_DEBUGF ((", connected!"));
396  socket->so_error = 0;
397  return (0);
398  }
399 
400  /* Don't let tcp_Retransmitter() timeout this socket
401  * (unless there is a ARP timeout etc.)
402  */
403  socket->tcp_sock->timeout = 0;
404 
405  if ((socket->so_state & (SS_ISDISCONNECTING | SS_CONN_REFUSED)) ||
406  (socket->tcp_sock->state >= tcp_StateCLOSED))
407  {
408  if (socket->so_error != 0 && socket->so_error != EALREADY)
409  {
410  SOCK_DEBUGF ((", %s", short_strerror(socket->so_error)));
411  SOCK_ERRNO (socket->so_error);
412  }
413  else if (chk_timeout(socket->nb_timer))
414  {
415  socket->so_state &= ~SS_ISCONNECTING;
416  SOCK_DEBUGF ((", ETIMEDOUT (%s)", socket->tcp_sock->err_msg));
417  socket->so_error = ETIMEDOUT;
418  SOCK_ERRNO (ETIMEDOUT);
419  }
420  else
421  {
422  SOCK_DEBUGF ((", ECONNREFUSED"));
423  socket->so_error = ECONNREFUSED;
424  SOCK_ERRNO (ECONNREFUSED); /* could also be ECONNRESET */
425  }
426  return (-1);
427  }
428 
429  if (socket->so_state & SS_ISCONNECTING)
430  {
431  SOCK_DEBUGF ((", EALREADY"));
432  socket->so_error = EALREADY; /* should be redundant */
433  SOCK_ERRNO (EALREADY);
434  return (-1);
435  }
436 
437  SOCK_FATAL (("%s (%d) Fatal: Unhandled non-block event\n",
438  __FILE__, __LINE__));
439  return (-1);
440 }
441 #endif /* USE_BSD_API */
int _sock_chk_sockaddr(Socket *socket, const struct sockaddr *sa, int len)
Check `sockaddr*' passed to bind/connect.
Definition: socket.c:839
int _TCP_open(Socket *socket, struct in_addr host, WORD loc_port, WORD rem_port)
Open and listen routines for SOCK_STREAM at the socket-level.
Definition: socket.c:2067
Definition: socket.h:137
Definition: if.h:84
DWORD W32_CALL set_timeout(DWORD msec)
Return time for when given timeout (msec) expires.
Definition: timer.c:503
Socket * _socklist_find(int s)
Returns a pointer to the Socket structure associated with socket 's'.
Definition: socket.c:1534
int _TCP6_open(Socket *socket, const void *dst, WORD loc_port, WORD rem_port)
Definition: socket.c:2110
int _UDP_open(Socket *socket, struct in_addr host, WORD loc_port, WORD rem_port)
Open and listen routines for SOCK_DGRAM at the socket-level.
Definition: socket.c:2003
void _sock_crit_start(void)
Start a critical region.
Definition: socket.c:1296
void _sock_crit_stop(void)
Mark the end of a critical region.
Definition: socket.c:1306
DWORD my_ip_addr
our IP address
Definition: pctcp.c:70
int _UDP6_open(Socket *socket, const void *host, WORD loc_port, WORD rem_port)
Definition: socket.c:2211
BOOL W32_CALL chk_timeout(DWORD value)
Check if milli-sec value has expired:
Definition: timer.c:547
DWORD timeout
timer for retrans etc.
Definition: wattcp.h:631
UINT state
tcp connection state
Definition: wattcp.h:622