curl/lib/connect.c

321 lines
7.8 KiB
C
Raw Normal View History

2001-10-01 16:59:17 +08:00
/*****************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* In order to be useful for every potential user, curl and libcurl are
* dual-licensed under the MPL and the MIT/X-derivate licenses.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the MPL or the MIT/X-derivate
* licenses. You may pick one of these licenses.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
*****************************************************************************/
#include "setup.h"
#ifndef WIN32
2001-10-01 19:25:27 +08:00
/* headers for non-win32 */
2001-10-01 16:59:17 +08:00
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
2001-10-01 19:25:27 +08:00
#ifdef HAVE_UNISTD_H
2001-10-01 16:59:17 +08:00
#include <unistd.h>
2001-10-01 19:25:27 +08:00
#endif
#ifdef HAVE_NETDB_H
2001-10-01 16:59:17 +08:00
#include <netdb.h>
2001-10-01 19:25:27 +08:00
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_NETINET_IN_H
2001-10-01 16:59:17 +08:00
#include <netinet/in.h>
2001-10-01 19:25:27 +08:00
#endif
2001-10-01 16:59:17 +08:00
#endif
#include <stdio.h>
#include <errno.h>
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#ifdef WIN32
#define HAVE_IOCTLSOCKET
#include <windows.h>
#include <winsock.h>
#define EINPROGRESS WSAEINPROGRESS
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#include "urldata.h"
#include "sendf.h"
/* The last #include file should be: */
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
/*************************************************************************
* Curl_nonblock
*
* Description:
* Set the socket to either blocking or non-blocking mode.
*/
static
int nonblock(int socket, /* operate on this */
2001-10-01 19:25:27 +08:00
int nonblock /* TRUE or FALSE */)
2001-10-01 16:59:17 +08:00
{
#undef SETBLOCK
#ifdef HAVE_O_NONBLOCK
int flags;
flags = fcntl(socket, F_GETFL, 0);
if (TRUE == nonblock)
return fcntl(socket, F_SETFL, flags | O_NONBLOCK);
else
return fcntl(socket, F_SETFL, flags & (~O_NONBLOCK));
#define SETBLOCK 1
#endif
#ifdef HAVE_FIONBIO
int flags;
flags = nonblock;
return ioctl(socket, FIONBIO, &flags);
#define SETBLOCK 2
#endif
#ifdef HAVE_IOCTLSOCKET
int flags;
flags = nonblock;
return ioctlsocket(socket, FIONBIO, &flags);
#define SETBLOCK 3
#endif
#ifdef HAVE_IOCTLSOCKET_CASE
return IoctlSocket(socket, FIONBIO, (long)nonblock);
#define SETBLOCK 4
#endif
#ifdef HAVE_DISABLED_NONBLOCKING
return 0; /* returns success */
#define SETBLOCK 5
#endif
#ifndef SETBLOCK
#error "no non-blocking method was found/used/set"
#endif
}
/*
* Return 0 on fine connect, -1 on error and 1 on timeout.
*/
static
int waitconnect(int sockfd, /* socket */
int timeout_msec)
{
fd_set fd;
struct timeval interval;
int rc;
/* now select() until we get connect or timeout */
FD_ZERO(&fd);
FD_SET(sockfd, &fd);
interval.tv_sec = timeout_msec/1000;
timeout_msec -= interval.tv_sec*1000;
interval.tv_usec = timeout_msec*1000;
rc = select(sockfd+1, NULL, &fd, NULL, &interval);
if(-1 == rc)
/* error, no connect here, try next */
return -1;
else if(0 == rc)
/* timeout, no connect today */
return 1;
/* we have a connect! */
return 0;
}
/*
* TCP connect to the given host with timeout, proxy or remote doesn't matter.
* There might be more than one IP address to try out. Fill in the passed
* pointer with the connected socket.
*/
CURLcode Curl_connecthost(struct connectdata *conn,
2001-10-01 19:25:27 +08:00
long timeout_ms,
Curl_addrinfo *remotehost,
int port,
2001-10-01 16:59:17 +08:00
int sockfd, /* input socket, or -1 if none */
int *socket)
{
struct SessionHandle *data = conn->data;
int rc;
2001-10-01 19:25:27 +08:00
struct timeval after;
struct timeval before = Curl_tvnow();
2001-10-01 16:59:17 +08:00
#ifdef ENABLE_IPV6
2001-10-02 06:42:46 +08:00
struct addrinfo *ai;
2001-10-01 16:59:17 +08:00
/*
* Connecting with IPv6 support is so much easier and cleanly done
*/
2001-10-02 06:50:03 +08:00
port =0; /* we already have port in the 'remotehost' struct */
2001-10-01 16:59:17 +08:00
if(sockfd != -1)
/* don't use any previous one, it might be of wrong type */
sclose(sockfd);
sockfd = -1; /* none! */
for (ai = remotehost; ai; ai = ai->ai_next) {
2001-10-01 16:59:17 +08:00
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0)
continue;
/* set socket non-blocking */
nonblock(sockfd, TRUE);
rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
if(0 == rc)
/* direct connect, awesome! */
break;
/* asynchronous connect, wait for connect or timeout */
2001-10-01 19:25:27 +08:00
rc = waitconnect(sockfd, timeout_ms);
2001-10-01 16:59:17 +08:00
if(0 != rc) {
/* connect failed or timed out */
sclose(sockfd);
sockfd = -1;
2001-10-01 19:25:27 +08:00
/* get a new timeout for next attempt */
after = Curl_tvnow();
timeout_ms -= (long)(Curl_tvdiff(after, before)*1000);
2001-10-02 07:25:26 +08:00
if(timeout_ms < 0)
break;
2001-10-01 19:25:27 +08:00
before = after;
2001-10-01 16:59:17 +08:00
continue;
}
/* now disable the non-blocking mode again */
nonblock(sockfd, FALSE);
break;
}
conn->ai = ai;
if (sockfd < 0) {
failf(data, strerror(errno));
return CURLE_COULDNT_CONNECT;
}
#else
/*
* Connecting with IPv4-only support
*/
int aliasindex;
struct sockaddr_in serv_addr;
if(-1 == sockfd)
/* create an ordinary socket if none was provided */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sockfd)
return CURLE_COULDNT_CONNECT; /* big time error */
2001-10-01 16:59:17 +08:00
/* non-block socket */
nonblock(sockfd, TRUE);
/* This is the loop that attempts to connect to all IP-addresses we
know for the given host. One by one. */
for(rc=-1, aliasindex=0;
rc && (struct in_addr *)remotehost->h_addr_list[aliasindex];
2001-10-01 16:59:17 +08:00
aliasindex++) {
/* copy this particular name info to the conn struct as it might
be used later in the krb4 "system" */
memset((char *) &serv_addr, '\0', sizeof(serv_addr));
memcpy((char *)&(serv_addr.sin_addr),
(struct in_addr *)remotehost->h_addr_list[aliasindex],
2001-10-01 16:59:17 +08:00
sizeof(struct in_addr));
serv_addr.sin_family = remotehost->h_addrtype;
serv_addr.sin_port = htons(port);
2001-10-01 16:59:17 +08:00
rc = connect(sockfd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr));
2001-10-01 16:59:17 +08:00
if(-1 == rc) {
int error;
#ifdef WIN32
error = (int)GetLastError();
#else
error = errno;
#endif
switch (error) {
case EINPROGRESS:
case EWOULDBLOCK:
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
/* On some platforms EAGAIN and EWOULDBLOCK are the
* same value, and on others they are different, hence
* the odd #if
*/
case EAGAIN:
#endif
/* asynchronous connect, wait for connect or timeout */
rc = waitconnect(sockfd, timeout_ms);
break;
default:
/* unknown error, fallthrough and try another address! */
break;
}
}
2001-10-01 19:25:27 +08:00
if(0 != rc) {
/* get a new timeout for next attempt */
after = Curl_tvnow();
timeout_ms -= (long)(Curl_tvdiff(after, before)*1000);
2001-10-02 07:25:26 +08:00
if(timeout_ms < 0)
break;
2001-10-01 19:25:27 +08:00
before = after;
2001-10-01 16:59:17 +08:00
continue; /* try next address */
2001-10-01 19:25:27 +08:00
}
else {
/* copy this particular name info to the conn struct as it might
be used later in the krb4 "system" */
memcpy((char *) &conn->serv_addr, &serv_addr,
sizeof(conn->serv_addr));
}
break;
2001-10-01 16:59:17 +08:00
}
if(-1 == rc) {
/* no good connect was made */
sclose(sockfd);
*socket = -1;
failf(data, "Couldn't connect to (any) IP address");
return CURLE_COULDNT_CONNECT;
}
/* now disable the non-blocking mode again */
nonblock(sockfd, FALSE);
#endif
*socket = sockfd; /* pass this to our parent */
return CURLE_OK;
}