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.82 2004/03/17 12:46:45 bagder 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 #include <sys/ioctl.h>
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
52 #include <stdlib.h> /* required for free() prototype, without it, this crashes
55 #if (defined(HAVE_FIONBIO) && defined(__NOVELL_LIBC__))
56 #include <sys/filio.h>
58 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
60 #define in_addr_t unsigned long
79 #define EINPROGRESS WSAEINPROGRESS
80 #define EWOULDBLOCK WSAEWOULDBLOCK
81 #define EISCONN WSAEISCONN
82 #define ENOTSOCK WSAENOTSOCK
83 #define ECONNREFUSED WSAECONNREFUSED
91 /* The last #include file should be: */
96 static bool verifyconnect(curl_socket_t sockfd);
98 int Curl_ourerrno(void)
101 return (int)GetLastError();
107 /*************************************************************************
111 * Set the socket to either blocking or non-blocking mode.
114 int Curl_nonblock(curl_socket_t sockfd, /* operate on this */
115 int nonblock /* TRUE or FALSE */)
118 #ifdef HAVE_O_NONBLOCK
119 /* most recent unix versions */
122 flags = fcntl(sockfd, F_GETFL, 0);
123 if (TRUE == nonblock)
124 return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
126 return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
131 /* older unix versions */
135 return ioctl(sockfd, FIONBIO, &flags);
139 #ifdef HAVE_IOCTLSOCKET
143 return ioctlsocket(sockfd, FIONBIO, &flags);
147 #ifdef HAVE_IOCTLSOCKET_CASE
148 /* presumably for Amiga */
149 return IoctlSocket(sockfd, FIONBIO, (long)nonblock);
153 #ifdef HAVE_SO_NONBLOCK
155 long b = nonblock ? 1 : 0;
156 return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b));
160 #ifdef HAVE_DISABLED_NONBLOCKING
161 return 0; /* returns success */
166 #error "no non-blocking method was found/used/set"
171 * waitconnect() returns:
175 * 2 select() returned with an error condition
178 int waitconnect(curl_socket_t sockfd, /* socket */
183 struct timeval interval;
186 /* Call this function once now, and ignore the results. We do this to
187 "clear" the error state on the socket so that we can later read it
188 reliably. This is reported necessary on the MPE/iX operating system. */
189 verifyconnect(sockfd);
192 /* now select() until we get connect or timeout */
197 FD_SET(sockfd, &errfd);
199 interval.tv_sec = timeout_msec/1000;
200 timeout_msec -= interval.tv_sec*1000;
202 interval.tv_usec = timeout_msec*1000;
204 rc = select(sockfd+1, NULL, &fd, &errfd, &interval);
206 /* error, no connect here, try next */
210 /* timeout, no connect today */
213 if(FD_ISSET(sockfd, &errfd))
214 /* error condition caught */
217 /* we have a connect! */
221 static CURLcode bindlocal(struct connectdata *conn,
222 curl_socket_t sockfd)
224 #ifdef HAVE_INET_NTOA
225 bool bindworked = FALSE;
226 struct SessionHandle *data = conn->data;
228 /*************************************************************
229 * Select device to bind socket to
230 *************************************************************/
231 if (strlen(data->set.device)<255) {
232 struct Curl_dns_entry *h=NULL;
234 char myhost[256] = "";
237 bool was_iface = FALSE;
239 /* First check if the given name is an IP address */
240 in=inet_addr(data->set.device);
242 if((in == CURL_INADDR_NONE) &&
243 Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
245 * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
247 rc = Curl_resolv(conn, myhost, 0, &h);
249 (void)Curl_wait_for_resolv(conn, &h);
257 * This was not an interface, resolve the name as a host name
260 rc = Curl_resolv(conn, data->set.device, 0, &h);
262 (void)Curl_wait_for_resolv(conn, &h);
265 /* we know data->set.device is shorter than the myhost array */
266 strcpy(myhost, data->set.device);
272 getmyhost(*myhost,sizeof(myhost)),
274 sizeof(hostent_buf));
276 failf(data, "Couldn't bind to '%s'", data->set.device);
277 return CURLE_HTTP_PORT_FAILED;
280 infof(data, "We bind local end to %s\n", myhost);
282 #ifdef SO_BINDTODEVICE
283 /* I am not sure any other OSs than Linux that provide this feature, and
284 * at the least I cannot test. --Ben
286 * This feature allows one to tightly bind the local socket to a
287 * particular interface. This will force even requests to other local
288 * interfaces to go out the external interface.
292 /* Only bind to the interface when specified as interface, not just as a
293 * hostname or ip address.
295 if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
296 data->set.device, strlen(data->set.device)+1) != 0) {
297 /* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n",
298 sockfd, data->set.device, strerror(errno)); */
299 infof(data, "SO_BINDTODEVICE %s failed\n",
301 /* This is typiclally "errno 1, error: Operation not permitted" if
302 you're not running as root or another suitable privileged user */
307 in=inet_addr(myhost);
308 if (CURL_INADDR_NONE != in) {
311 Curl_addrinfo *addr = h->addr;
313 Curl_resolv_unlock(data, h);
314 /* we don't need it anymore after this function has returned */
317 if( bind(sockfd, addr->ai_addr, addr->ai_addrlen) >= 0) {
318 /* we succeeded to bind */
319 struct sockaddr_in6 add;
324 if(getsockname(sockfd, (struct sockaddr *) &add,
325 (socklen_t *)&size)<0) {
326 failf(data, "getsockname() failed");
327 return CURLE_HTTP_PORT_FAILED;
332 struct sockaddr_in sa;
334 memset((char *)&sa, 0, sizeof(sa));
335 memcpy((char *)&sa.sin_addr, addr->h_addr, addr->h_length);
336 sa.sin_family = AF_INET;
337 sa.sin_addr.s_addr = in;
338 sa.sin_port = 0; /* get any port */
340 if( bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) >= 0) {
341 /* we succeeded to bind */
342 struct sockaddr_in add;
347 if(getsockname(sockfd, (struct sockaddr *) &add,
348 (socklen_t *)&size)<0) {
349 failf(data, "getsockname() failed");
350 return CURLE_HTTP_PORT_FAILED;
358 failf(data, "Invalid descriptor: %d", errno);
361 failf(data, "Invalid request: %d", errno);
364 failf(data, "Address is protected, user not superuser: %d", errno);
368 "Argument is a descriptor for a file, not a socket: %d",
372 failf(data, "Inaccessable memory error: %d", errno);
375 failf(data, "Address too long: %d", errno);
378 failf(data, "Insufficient kernel memory was available: %d", errno);
381 failf(data, "errno %d", errno);
383 } /* end of switch(errno) */
385 return CURLE_HTTP_PORT_FAILED;
390 failf(data,"could't find my own IP address (%s)", myhost);
391 return CURLE_HTTP_PORT_FAILED;
393 } /* end of inet_addr */
396 failf(data, "could't find my own IP address (%s)", myhost);
397 return CURLE_HTTP_PORT_FAILED;
402 } /* end of device selection support */
403 #endif /* end of HAVE_INET_NTOA */
405 return CURLE_HTTP_PORT_FAILED;
409 * verifyconnect() returns TRUE if the connect really has happened.
411 static bool verifyconnect(curl_socket_t sockfd)
413 #if defined(SO_ERROR) && !defined(WIN32)
415 socklen_t errSize = sizeof(err);
416 if( -1 == getsockopt(sockfd, SOL_SOCKET, SO_ERROR,
417 (void *)&err, &errSize))
418 err = Curl_ourerrno();
420 if ((0 == err) || (EISCONN == err))
421 /* we are connected, awesome! */
424 /* This wasn't a successful connect */
433 * Curl_is_connected() is used from the multi interface to check if the
434 * firstsocket has connected.
437 CURLcode Curl_is_connected(struct connectdata *conn,
438 curl_socket_t sockfd,
442 struct SessionHandle *data = conn->data;
444 *connected = FALSE; /* a very negative world view is best */
446 if(data->set.timeout || data->set.connecttimeout) {
447 /* there is a timeout set */
449 /* Evaluate in milliseconds how much time that has passed */
450 long has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
452 /* subtract the most strict timeout of the ones */
453 if(data->set.timeout && data->set.connecttimeout) {
454 if (data->set.timeout < data->set.connecttimeout)
455 has_passed -= data->set.timeout*1000;
457 has_passed -= data->set.connecttimeout*1000;
459 else if(data->set.timeout)
460 has_passed -= data->set.timeout*1000;
462 has_passed -= data->set.connecttimeout*1000;
464 if(has_passed > 0 ) {
465 /* time-out, bail out, go home */
466 failf(data, "Connection time-out");
467 return CURLE_OPERATION_TIMEOUTED;
470 if(conn->bits.tcpconnect) {
471 /* we are connected already! */
476 /* check for connect without timeout as we want to return immediately */
477 rc = waitconnect(sockfd, 0);
480 if (verifyconnect(sockfd)) {
481 /* we are connected, awesome! */
485 /* nope, not connected for real */
486 failf(data, "Connection failed");
487 return CURLE_COULDNT_CONNECT;
490 int error = Curl_ourerrno();
491 failf(data, "Failed connect to %s:%d, errno: %d",
492 conn->hostname, conn->port, error);
493 return CURLE_COULDNT_CONNECT;
496 * If the connection phase is "done" here, we should attempt to connect
497 * to the "next address" in the Curl_hostaddr structure that we resolved
498 * before. But we don't have that struct around anymore and we can't just
499 * keep a pointer since the cache might in fact have gotten pruned by the
500 * time we want to read this... Alas, we don't do this yet.
508 * TCP connect to the given host with timeout, proxy or remote doesn't matter.
509 * There might be more than one IP address to try out. Fill in the passed
510 * pointer with the connected socket.
513 CURLcode Curl_connecthost(struct connectdata *conn, /* context */
514 struct Curl_dns_entry *remotehost, /* use this one */
515 int port, /* connect to this */
516 curl_socket_t *sockconn, /* the connected socket */
517 Curl_ipconnect **addr, /* the one we used */
518 bool *connected) /* really connected? */
520 struct SessionHandle *data = conn->data;
522 curl_socket_t sockfd= CURL_SOCKET_BAD;
526 struct timeval after;
527 struct timeval before = Curl_tvnow();
533 /*************************************************************
534 * Figure out what maximum time we have left
535 *************************************************************/
536 long timeout_ms=300000; /* milliseconds, default to five minutes */
538 *connected = FALSE; /* default to not connected */
540 if(data->set.timeout || data->set.connecttimeout) {
543 /* Evaluate in milliseconds how much time that has passed */
544 has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
547 #define min(a, b) ((a) < (b) ? (a) : (b))
550 /* get the most strict timeout of the ones converted to milliseconds */
551 if(data->set.timeout && data->set.connecttimeout) {
552 if (data->set.timeout < data->set.connecttimeout)
553 timeout_ms = data->set.timeout*1000;
555 timeout_ms = data->set.connecttimeout*1000;
557 else if(data->set.timeout)
558 timeout_ms = data->set.timeout*1000;
560 timeout_ms = data->set.connecttimeout*1000;
562 /* subtract the passed time */
563 timeout_ms -= (long)has_passed;
566 /* a precaution, no need to continue if time already is up */
567 failf(data, "Connection time-out");
568 return CURLE_OPERATION_TIMEOUTED;
572 hostname = data->change.proxy?conn->proxyhost:conn->hostname;
573 infof(data, "About to connect() to %s port %d\n",
578 * Connecting with a getaddrinfo chain
580 for (ai = remotehost->addr; ai; ai = ai->ai_next, aliasindex++) {
581 sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
582 if (sockfd == CURL_SOCKET_BAD)
586 * Connecting with old style IPv4-only support
589 /* This is the loop that attempts to connect to all IP-addresses we
590 know for the given host. One by one. */
591 for(rc=-1, aliasindex=0;
592 rc && (struct in_addr *)remotehost->addr->h_addr_list[aliasindex];
594 struct sockaddr_in serv_addr;
596 /* create an IPv4 TCP socket */
597 sockfd = socket(AF_INET, SOCK_STREAM, 0);
598 if(CURL_SOCKET_BAD == sockfd) {
599 failf(data, "couldn't create socket");
600 return CURLE_COULDNT_CONNECT; /* big time error */
603 /* nasty address work before connect can be made */
604 memset((char *) &serv_addr, '\0', sizeof(serv_addr));
605 memcpy((char *)&(serv_addr.sin_addr),
606 (struct in_addr *)remotehost->addr->h_addr_list[aliasindex],
607 sizeof(struct in_addr));
608 serv_addr.sin_family = remotehost->addr->h_addrtype;
609 serv_addr.sin_port = htons((unsigned short)port);
612 if(conn->data->set.device) {
613 /* user selected to bind the outgoing socket to a specified "device"
614 before doing connect */
615 CURLcode res = bindlocal(conn, sockfd);
620 /* set socket non-blocking */
621 Curl_nonblock(sockfd, TRUE);
623 /* do not use #ifdef within the function arguments below, as connect() is
624 a defined macro on some platforms and some compilers don't like to mix
625 #ifdefs with macro usage! (AmigaOS is one such platform) */
627 rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
629 rc = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
633 int error=Curl_ourerrno();
638 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
639 /* On some platforms EAGAIN and EWOULDBLOCK are the
640 * same value, and on others they are different, hence
645 /* asynchronous connect, wait for connect or timeout */
646 if(data->state.used_interface == Curl_if_multi)
647 /* don't hang when doing multi */
650 rc = waitconnect(sockfd, timeout_ms);
653 /* unknown error, fallthrough and try another address! */
654 failf(data, "Failed to connect to %s IP number %d: %d",
655 hostname, aliasindex+1, error);
660 /* The '1 == rc' comes from the waitconnect(), and not from connect().
661 We can be sure of this since connect() cannot return 1. */
662 if((1 == rc) && (data->state.used_interface == Curl_if_multi)) {
663 /* Timeout when running the multi interface, we return here with a
664 CURLE_OK return code. */
670 if (verifyconnect(sockfd)) {
671 /* we are connected, awesome! */
672 *connected = TRUE; /* this is a true connect */
675 /* nope, not connected for real */
679 /* connect failed or timed out */
683 /* get a new timeout for next attempt */
684 after = Curl_tvnow();
685 timeout_ms -= Curl_tvdiff(after, before);
687 failf(data, "connect() timed out!");
688 return CURLE_OPERATION_TIMEOUTED;
692 if (sockfd == CURL_SOCKET_BAD) {
693 /* no good connect was made */
695 failf(data, "Connect failed");
696 return CURLE_COULDNT_CONNECT;
699 /* leave the socket in non-blocking mode */
701 /* store the address we use */
706 *addr = (struct in_addr *)remotehost->addr->h_addr_list[aliasindex];
710 /* allow NULL-pointers to get passed in */
712 *sockconn = sockfd; /* the socket descriptor we've connected */