Newer
Older
/*****************************************************************************
* _ _ ____ _
* 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
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_NETINET_IN_H
#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 */
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
{
#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,
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();
/*
* Connecting with IPv6 support is so much easier and cleanly done
*/
port =0; /* we already have port in the 'remotehost' struct */
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 */
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],
serv_addr.sin_family = remotehost->h_addrtype;
serv_addr.sin_port = htons(port);
rc = connect(sockfd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr));
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
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;
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;
}