1
/***************************************************************************
3
* Project ___| | | | _ \| |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
8
* Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
10
* This software is licensed as described in the file COPYING, which
11
* you should have received as part of this distribution. The terms
12
* are also available at http://curl.haxx.se/docs/copyright.html.
14
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
* copies of the Software, and permit persons to whom the Software is
16
* furnished to do so, under the terms of the COPYING file.
18
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
* KIND, either express or implied.
21
* $Id: connect.c,v 1.18 2004/10/13 14:01:04 andy Exp $
22
***************************************************************************/
27
/* headers for non-win32 */
29
#ifdef HAVE_SYS_TYPES_H
30
#include <sys/types.h>
32
#ifdef HAVE_SYS_SOCKET_H
33
#include <sys/socket.h>
35
#ifdef HAVE_NETINET_IN_H
36
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
38
#ifdef HAVE_NETINET_TCP_H
39
#include <netinet/tcp.h> /* for TCP_NODELAY */
41
#ifdef HAVE_SYS_IOCTL_H
42
#include <sys/ioctl.h>
53
#ifdef HAVE_NETINET_IN_H
54
#include <netinet/in.h>
56
#ifdef HAVE_ARPA_INET_H
57
#include <arpa/inet.h>
60
#include <stdlib.h> /* required for free() prototype, without it, this crashes
63
#if (defined(HAVE_FIONBIO) && defined(__NOVELL_LIBC__))
64
#include <sys/filio.h>
66
#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
68
#define in_addr_t unsigned long
87
#define EINPROGRESS WSAEINPROGRESS
88
#define EWOULDBLOCK WSAEWOULDBLOCK
89
#define EISCONN WSAEISCONN
90
#define ENOTSOCK WSAENOTSOCK
91
#define ECONNREFUSED WSAECONNREFUSED
99
#include "curl_memory.h"
101
/* The last #include file should be: */
102
#include "memdebug.h"
104
static bool verifyconnect(curl_socket_t sockfd, int *error);
107
singleipconnect(struct connectdata *conn,
108
Curl_addrinfo *ai, /* start connecting to this */
113
* Curl_ourerrno() returns the errno (or equivalent) on this platform to
114
* hide platform specific for the function that calls this.
116
int Curl_ourerrno(void)
119
return (int)GetLastError();
126
* Curl_nonblock() set the given socket to either blocking or non-blocking
127
* mode based on the 'nonblock' boolean argument. This function is highly
130
int Curl_nonblock(curl_socket_t sockfd, /* operate on this */
131
int nonblock /* TRUE or FALSE */)
134
#ifdef HAVE_O_NONBLOCK
136
/* most recent unix versions */
139
flags = fcntl(sockfd, F_GETFL, 0);
140
if (TRUE == nonblock)
141
return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
143
return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
150
/* older unix versions */
154
return ioctl(sockfd, FIONBIO, &flags);
162
#ifdef HAVE_IOCTLSOCKET
166
return ioctlsocket(sockfd, FIONBIO, &flags);
170
#ifdef HAVE_IOCTLSOCKET_CASE
171
/* presumably for Amiga */
172
return IoctlSocket(sockfd, FIONBIO, (long)nonblock);
179
#ifdef HAVE_SO_NONBLOCK
181
long b = nonblock ? 1 : 0;
182
return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
189
#ifdef HAVE_DISABLED_NONBLOCKING
192
return 0; /* returns success */
200
#error "no non-blocking method was found/used/set"
205
* waitconnect() waits for a TCP connect on the given socket for the specified
206
* number if milliseconds. It returns:
210
* 2 select() returned with an error condition fd_set
213
#define WAITCONN_CONNECTED 0
214
#define WAITCONN_SELECT_ERROR -1
215
#define WAITCONN_TIMEOUT 1
216
#define WAITCONN_FDSET_ERROR 2
219
int waitconnect(curl_socket_t sockfd, /* socket */
224
struct timeval interval;
227
/* Call this function once now, and ignore the results. We do this to
228
"clear" the error state on the socket so that we can later read it
229
reliably. This is reported necessary on the MPE/iX operating system. */
230
verifyconnect(sockfd, NULL);
233
/* now select() until we get connect or timeout */
238
FD_SET(sockfd, &errfd);
240
interval.tv_sec = (int)(timeout_msec/1000);
241
timeout_msec -= interval.tv_sec*1000;
243
interval.tv_usec = timeout_msec*1000;
245
rc = select(sockfd+1, NULL, &fd, &errfd, &interval);
247
/* error, no connect here, try next */
248
return WAITCONN_SELECT_ERROR;
251
/* timeout, no connect today */
252
return WAITCONN_TIMEOUT;
254
if(FD_ISSET(sockfd, &errfd))
255
/* error condition caught */
256
return WAITCONN_FDSET_ERROR;
258
/* we have a connect! */
259
return WAITCONN_CONNECTED;
262
static CURLcode bindlocal(struct connectdata *conn,
263
curl_socket_t sockfd)
265
#ifdef HAVE_INET_NTOA
266
bool bindworked = FALSE;
267
struct SessionHandle *data = conn->data;
269
/*************************************************************
270
* Select device to bind socket to
271
*************************************************************/
272
if (strlen(data->set.device)<255) {
273
struct Curl_dns_entry *h=NULL;
274
char myhost[256] = "";
277
bool was_iface = FALSE;
279
/* First check if the given name is an IP address */
280
in=inet_addr(data->set.device);
282
if((in == CURL_INADDR_NONE) &&
283
Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
285
* We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
287
rc = Curl_resolv(conn, myhost, 0, &h);
288
if(rc == CURLRESOLV_PENDING)
289
(void)Curl_wait_for_resolv(conn, &h);
297
* This was not an interface, resolve the name as a host name
300
rc = Curl_resolv(conn, data->set.device, 0, &h);
301
if(rc == CURLRESOLV_PENDING)
302
(void)Curl_wait_for_resolv(conn, &h);
305
/* we know data->set.device is shorter than the myhost array */
306
strcpy(myhost, data->set.device);
312
getmyhost(*myhost,sizeof(myhost)),
314
sizeof(hostent_buf));
316
failf(data, "Couldn't bind to '%s'", data->set.device);
317
return CURLE_HTTP_PORT_FAILED;
320
infof(data, "We bind local end to %s\n", myhost);
322
#ifdef SO_BINDTODEVICE
323
/* I am not sure any other OSs than Linux that provide this feature, and
324
* at the least I cannot test. --Ben
326
* This feature allows one to tightly bind the local socket to a
327
* particular interface. This will force even requests to other local
328
* interfaces to go out the external interface.
332
/* Only bind to the interface when specified as interface, not just as a
333
* hostname or ip address.
335
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
336
data->set.device, strlen(data->set.device)+1) != 0) {
337
/* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n",
338
sockfd, data->set.device, Curl_strerror(Curl_ourerrno())); */
339
infof(data, "SO_BINDTODEVICE %s failed\n",
341
/* This is typically "errno 1, error: Operation not permitted" if
342
you're not running as root or another suitable privileged user */
347
in=inet_addr(myhost);
348
if (CURL_INADDR_NONE != in) {
351
Curl_addrinfo *addr = h->addr;
353
Curl_resolv_unlock(data, h);
354
/* we don't need it anymore after this function has returned */
356
if( bind(sockfd, addr->ai_addr, (socklen_t)addr->ai_addrlen) >= 0) {
357
/* we succeeded to bind */
359
struct sockaddr_in6 add;
361
struct sockaddr_in add;
365
int gsize = sizeof(add);
367
socklen_t gsize = sizeof(add);
371
if(getsockname(sockfd, (struct sockaddr *) &add,
373
failf(data, "getsockname() failed");
374
return CURLE_HTTP_PORT_FAILED;
379
failf(data, "%s", Curl_strerror(conn, Curl_ourerrno()));
380
return CURLE_HTTP_PORT_FAILED;
385
failf(data,"could't find my own IP address (%s)", myhost);
386
return CURLE_HTTP_PORT_FAILED;
388
} /* end of inet_addr */
391
failf(data, "could't find my own IP address (%s)", myhost);
392
return CURLE_HTTP_PORT_FAILED;
397
} /* end of device selection support */
398
#endif /* end of HAVE_INET_NTOA */
400
return CURLE_HTTP_PORT_FAILED;
404
* verifyconnect() returns TRUE if the connect really has happened.
406
static bool verifyconnect(curl_socket_t sockfd, int *error)
412
int errSize = sizeof(err);
414
socklen_t errSize = sizeof(err);
420
* In October 2003 we effectively nullified this function on Windows due to
421
* problems with it using all CPU in multi-threaded cases.
423
* In May 2004, we bring it back to offer more info back on connect failures.
424
* Gisle Vanem could reproduce the former problems with this function, but
425
* could avoid them by adding this SleepEx() call below:
427
* "I don't have Rational Quantify, but the hint from his post was
428
* ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
429
* just Sleep(0) would be enough?) would release whatever
430
* mutex/critical-section the ntdll call is waiting on.
432
* Someone got to verify this on Win-NT 4.0, 2000."
437
if( -1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR,
438
(void *)&err, &errSize))
439
err = Curl_ourerrno();
441
if ((0 == err) || (EISCONN == err))
442
/* we are connected, awesome! */
445
/* This wasn't a successful connect */
452
*error = Curl_ourerrno();
457
/* Used within the multi interface. Try next IP address, return TRUE if no
458
more address exists */
459
static bool trynextip(struct connectdata *conn,
463
curl_socket_t sockfd;
466
if(sockindex != FIRSTSOCKET)
467
return TRUE; /* no next */
469
/* try the next address */
470
ai = conn->ip_addr->ai_next;
473
sockfd = singleipconnect(conn, ai, 0L, connected);
474
if(sockfd != CURL_SOCKET_BAD) {
475
/* store the new socket descriptor */
476
conn->sock[sockindex] = sockfd;
486
* Curl_is_connected() is used from the multi interface to check if the
487
* firstsocket has connected.
490
CURLcode Curl_is_connected(struct connectdata *conn,
495
struct SessionHandle *data = conn->data;
496
CURLcode code = CURLE_OK;
497
curl_socket_t sockfd = conn->sock[sockindex];
498
long allow = DEFAULT_CONNECT_TIMEOUT;
501
curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
503
*connected = FALSE; /* a very negative world view is best */
505
/* Evaluate in milliseconds how much time that has passed */
506
has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
508
/* subtract the most strict timeout of the ones */
509
if(data->set.timeout && data->set.connecttimeout) {
510
if (data->set.timeout < data->set.connecttimeout)
511
allow = data->set.timeout*1000;
513
allow = data->set.connecttimeout*1000;
515
else if(data->set.timeout) {
516
allow = data->set.timeout*1000;
518
else if(data->set.connecttimeout) {
519
allow = data->set.connecttimeout*1000;
522
if(has_passed > allow ) {
523
/* time-out, bail out, go home */
524
failf(data, "Connection time-out after %ld ms", has_passed);
525
return CURLE_OPERATION_TIMEOUTED;
527
if(conn->bits.tcpconnect) {
528
/* we are connected already! */
533
/* check for connect without timeout as we want to return immediately */
534
rc = waitconnect(sockfd, 0);
536
if(WAITCONN_CONNECTED == rc) {
537
if (verifyconnect(sockfd, NULL)) {
538
/* we are connected, awesome! */
542
/* nope, not connected for real */
543
infof(data, "Connection failed\n");
544
if(trynextip(conn, sockindex, connected)) {
545
code = CURLE_COULDNT_CONNECT;
548
else if(WAITCONN_TIMEOUT != rc) {
549
/* nope, not connected */
550
infof(data, "Connection failed\n");
551
if(trynextip(conn, sockindex, connected)) {
552
int error = Curl_ourerrno();
553
failf(data, "Failed connect to %s:%d; %s",
554
conn->host.name, conn->port, Curl_strerror(conn,error));
555
code = CURLE_COULDNT_CONNECT;
559
* If the connection failed here, we should attempt to connect to the "next
560
* address" for the given host.
566
static void tcpnodelay(struct connectdata *conn,
567
curl_socket_t sockfd)
570
struct SessionHandle *data= conn->data;
571
socklen_t onoff = (socklen_t) data->set.tcp_nodelay;
572
if(setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&onoff,
574
infof(data, "Could not set TCP_NODELAY: %s\n",
575
Curl_strerror(conn, Curl_ourerrno()));
577
infof(data,"TCP_NODELAY set\n");
584
/* singleipconnect() connects to the given IP only, and it may return without
585
having connected if used from the multi interface. */
587
singleipconnect(struct connectdata *conn,
596
struct SessionHandle *data = conn->data;
597
curl_socket_t sockfd = socket(ai->ai_family, ai->ai_socktype,
599
if (sockfd == CURL_SOCKET_BAD)
600
return CURL_SOCKET_BAD;
602
*connected = FALSE; /* default is not connected */
604
Curl_printable_address(ai, addr_buf, sizeof(addr_buf));
605
infof(data, " Trying %s... ", addr_buf);
607
if(data->set.tcp_nodelay)
608
tcpnodelay(conn, sockfd);
610
if(conn->data->set.device) {
611
/* user selected to bind the outgoing socket to a specified "device"
612
before doing connect */
613
CURLcode res = bindlocal(conn, sockfd);
618
/* set socket non-blocking */
619
Curl_nonblock(sockfd, TRUE);
621
rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
624
error = Curl_ourerrno();
629
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
630
/* On some platforms EAGAIN and EWOULDBLOCK are the
631
* same value, and on others they are different, hence
636
rc = waitconnect(sockfd, timeout_ms);
639
/* unknown error, fallthrough and try another address! */
640
failf(data, "Failed to connect to %s: %s",
641
addr_buf, Curl_strerror(conn,error));
646
/* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from
647
connect(). We can be sure of this since connect() cannot return 1. */
648
if((WAITCONN_TIMEOUT == rc) &&
649
(data->state.used_interface == Curl_if_multi)) {
650
/* Timeout when running the multi interface */
654
conected = verifyconnect(sockfd, &error);
656
if(!rc && conected) {
657
/* we are connected, awesome! */
658
*connected = TRUE; /* this is a true connect */
659
infof(data, "connected\n");
662
else if(WAITCONN_TIMEOUT == rc)
663
infof(data, "Timeout\n");
665
infof(data, "%s\n", Curl_strerror(conn, error));
667
/* connect failed or timed out */
670
return CURL_SOCKET_BAD;
674
* TCP connect to the given host with timeout, proxy or remote doesn't matter.
675
* There might be more than one IP address to try out. Fill in the passed
676
* pointer with the connected socket.
679
CURLcode Curl_connecthost(struct connectdata *conn, /* context */
680
struct Curl_dns_entry *remotehost, /* use this one */
681
curl_socket_t *sockconn, /* the connected socket */
682
Curl_addrinfo **addr, /* the one we used */
683
bool *connected) /* really connected? */
685
struct SessionHandle *data = conn->data;
686
curl_socket_t sockfd = CURL_SOCKET_BAD;
690
Curl_addrinfo *curr_addr;
692
struct timeval after;
693
struct timeval before = Curl_tvnow();
695
/*************************************************************
696
* Figure out what maximum time we have left
697
*************************************************************/
698
long timeout_ms= DEFAULT_CONNECT_TIMEOUT;
699
long timeout_per_addr;
701
*connected = FALSE; /* default to not connected */
703
if(data->set.timeout || data->set.connecttimeout) {
706
/* Evaluate in milliseconds how much time that has passed */
707
has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
710
#define min(a, b) ((a) < (b) ? (a) : (b))
713
/* get the most strict timeout of the ones converted to milliseconds */
714
if(data->set.timeout && data->set.connecttimeout) {
715
if (data->set.timeout < data->set.connecttimeout)
716
timeout_ms = data->set.timeout*1000;
718
timeout_ms = data->set.connecttimeout*1000;
720
else if(data->set.timeout)
721
timeout_ms = data->set.timeout*1000;
723
timeout_ms = data->set.connecttimeout*1000;
725
/* subtract the passed time */
726
timeout_ms -= has_passed;
729
/* a precaution, no need to continue if time already is up */
730
failf(data, "Connection time-out");
731
return CURLE_OPERATION_TIMEOUTED;
735
/* Max time for each address */
736
num_addr = Curl_num_addresses(remotehost->addr);
737
timeout_per_addr = timeout_ms / num_addr;
739
ai = remotehost->addr;
741
/* Below is the loop that attempts to connect to all IP-addresses we
742
* know for the given host. One by one until one IP succeeds.
745
if(data->state.used_interface == Curl_if_multi)
746
/* don't hang when doing multi */
747
timeout_per_addr = timeout_ms = 0;
750
* Connecting with a Curl_addrinfo chain
752
for (curr_addr = ai, aliasindex=0; curr_addr;
753
curr_addr = curr_addr->ai_next, aliasindex++) {
755
/* start connecting to the IP curr_addr points to */
756
sockfd = singleipconnect(conn, curr_addr, timeout_per_addr, connected);
758
if(sockfd != CURL_SOCKET_BAD)
761
/* get a new timeout for next attempt */
762
after = Curl_tvnow();
763
timeout_ms -= Curl_tvdiff(after, before);
765
failf(data, "connect() timed out!");
766
return CURLE_OPERATION_TIMEOUTED;
769
} /* end of connect-to-each-address loop */
771
if (sockfd == CURL_SOCKET_BAD) {
772
/* no good connect was made */
773
*sockconn = CURL_SOCKET_BAD;
774
return CURLE_COULDNT_CONNECT;
777
/* leave the socket in non-blocking mode */
779
/* store the address we use */
783
/* allow NULL-pointers to get passed in */
785
*sockconn = sockfd; /* the socket descriptor we've connected */