mirror of
https://github.com/curl/curl.git
synced 2024-11-21 01:16:58 +08:00
315 lines
7.7 KiB
C
315 lines
7.7 KiB
C
/*****************************************************************************
|
|
* _ _ ____ _
|
|
* 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
|
|
/* headers for non-win32 */
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_NETDB_H
|
|
#include <netdb.h>
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
#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 */
|
|
int nonblock /* TRUE or FALSE */)
|
|
{
|
|
#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,
|
|
long timeout_ms,
|
|
Curl_addrinfo *remotehost,
|
|
int port,
|
|
int sockfd, /* input socket, or -1 if none */
|
|
int *socket)
|
|
{
|
|
struct SessionHandle *data = conn->data;
|
|
int rc;
|
|
|
|
struct timeval after;
|
|
struct timeval before = Curl_tvnow();
|
|
|
|
#ifdef ENABLE_IPV6
|
|
struct addrinfo *ai;
|
|
/*
|
|
* Connecting with IPv6 support is so much easier and cleanly done
|
|
*/
|
|
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) {
|
|
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 */
|
|
rc = waitconnect(sockfd, timeout_ms);
|
|
if(0 != rc) {
|
|
/* connect failed or timed out */
|
|
sclose(sockfd);
|
|
sockfd = -1;
|
|
|
|
/* get a new timeout for next attempt */
|
|
after = Curl_tvnow();
|
|
timeout_ms -= (long)(Curl_tvdiff(after, before)*1000);
|
|
before = after;
|
|
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 */
|
|
|
|
/* 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];
|
|
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],
|
|
sizeof(struct in_addr));
|
|
serv_addr.sin_family = remotehost->h_addrtype;
|
|
serv_addr.sin_port = htons(port);
|
|
|
|
rc = connect(sockfd, (struct sockaddr *)&serv_addr,
|
|
sizeof(serv_addr));
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if(0 != rc) {
|
|
/* get a new timeout for next attempt */
|
|
after = Curl_tvnow();
|
|
timeout_ms -= (long)(Curl_tvdiff(after, before)*1000);
|
|
before = after;
|
|
continue; /* try next address */
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|