Commit c5fdeef4 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

introduced non-blocking connects

parent 6ca45bea
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -59,7 +59,7 @@ escape.c mprintf.c telnet.c \
escape.h       getpass.c      netrc.c        telnet.h       \
getinfo.c getinfo.h transfer.c strequal.c strequal.h easy.c \
security.h security.c krb4.c krb4.h memdebug.c memdebug.h inet_ntoa_r.h \
http_chunks.c http_chunks.h strtok.c strtok.h
http_chunks.c http_chunks.h strtok.c strtok.h connect.c connect.h

noinst_HEADERS = setup.h transfer.h

lib/connect.c

0 → 100644
+275 −0
Original line number Diff line number Diff line
/*****************************************************************************
 *                                  _   _ ____  _     
 *  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>
#include <unistd.h>
#include <netdb.h>
#include <sys/fcntl.h>
#include <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 */
                  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,
                          int sockfd, /* input socket, or -1 if none */
                          int *socket)
{
  struct SessionHandle *data = conn->data;
  int rc;

#ifdef ENABLE_IPV6
  /*
   * 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 = conn->hp; 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);
    if(0 != rc) {
      /* connect failed or timed out */
      sclose(sockfd);
      sockfd = -1;
      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;
  int timeout_ms = 10000; /* while testing */

  /* 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 *)conn->hp->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 *) &conn->serv_addr, '\0', sizeof(conn->serv_addr));
    memcpy((char *)&(conn->serv_addr.sin_addr),
           (struct in_addr *)conn->hp->h_addr_list[aliasindex],
           sizeof(struct in_addr));
    conn->serv_addr.sin_family = conn->hp->h_addrtype;
    conn->serv_addr.sin_port = htons(conn->port);
  
    rc = connect(sockfd, (struct sockaddr *)&(conn->serv_addr),
                 sizeof(conn->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)
      continue; /* try next address */
    else
      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;
}

lib/connect.h

0 → 100644
+29 −0
Original line number Diff line number Diff line
#ifndef __NONBLOCK_H
#define __NONBLOCK_H
/*****************************************************************************
 *                                  _   _ ____  _     
 *  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$
 *****************************************************************************/

CURLcode Curl_connecthost(struct connectdata *conn,
                          int sockfd, /* input socket, or -1 if none */
                          int *socket);
#endif
+8 −107
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@
#include "file.h"
#include "ldap.h"
#include "url.h"
#include "connect.h"

#include <curl/types.h>

@@ -1123,12 +1124,6 @@ static CURLcode ConnectPlease(struct SessionHandle *data,

#ifndef ENABLE_IPV6
  conn->firstsocket = socket(AF_INET, SOCK_STREAM, 0);

  memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr));
  memcpy((char *)&(conn->serv_addr.sin_addr),
         conn->hp->h_addr, conn->hp->h_length);
  conn->serv_addr.sin_family = conn->hp->h_addrtype;
  conn->serv_addr.sin_port = htons(conn->port);
#else
  /* IPv6-style */
  struct addrinfo *ai;
@@ -1259,108 +1254,14 @@ static CURLcode ConnectPlease(struct SessionHandle *data,
  /*************************************************************
   * Connect to server/proxy
   *************************************************************/
#ifdef ENABLE_IPV6
  conn->firstsocket = -1;
  for (ai = conn->hp; ai; ai = ai->ai_next) {
    conn->firstsocket = socket(ai->ai_family,
                               ai->ai_socktype,
                               ai->ai_protocol);
    if (conn->firstsocket < 0)
      continue;

    if (connect(conn->firstsocket, ai->ai_addr, ai->ai_addrlen) < 0) {
      sclose(conn->firstsocket);
      conn->firstsocket = -1;
      continue;
    }

    break;
  }
  conn->ai = ai;
  if (conn->firstsocket < 0) {
    failf(data, strerror(errno));
    return CURLE_COULDNT_CONNECT;
  }
#else
  /* non-zero nonblock value sets socket as nonblocking under Win32 */
#if defined(WIN32)
  FD_ZERO (&connectfd);
  FD_SET(conn->firstsocket, &connectfd);
  if (conn->data->set.connecttimeout > 0) {
    nonblock = 1;
  }
  ioctlsocket(conn->firstsocket, FIONBIO, &nonblock);
#endif
  if (connect(conn->firstsocket,
              (struct sockaddr *) &(conn->serv_addr),
              sizeof(conn->serv_addr)
              ) < 0) {
#if defined(WIN32)
    conntimeout.tv_sec = conn->data->set.connecttimeout;
    conntimeout.tv_usec = 0;	
    if(-1 != select (conn->firstsocket + 1, NULL, &connectfd, NULL, &conntimeout)) {
      if (FD_ISSET(conn->firstsocket, &connectfd)) {
        /* shut off non-blocking again */
        nonblock = 0;
        ioctlsocket(conn->firstsocket, FIONBIO, &nonblock);
        return CURLE_OK;
      }
      else
        errno = EINTR;
    }
#endif
    switch(errno) {
#ifdef ECONNREFUSED
      /* this should be made nicer */
    case ECONNREFUSED:
      failf(data, "Connection refused");
      break;
    case EFAULT:
      failf(data, "Invalid socket address: %d",errno);
      break;
    case EISCONN:
      failf(data, "Socket already connected: %d",errno);
      break;
    case ETIMEDOUT:
      failf(data, "Timeout while accepting connection, server busy: %d",errno);
      break;
    case ENETUNREACH:
      failf(data, "Network is unreachable: %d",errno);
      break;
    case EADDRINUSE:
      failf(data, "Local address already in use: %d",errno);
      break;
    case EINPROGRESS:
      failf(data, "Socket is nonblocking and connection can not be completed immediately: %d",errno);
      break;
    case EALREADY:
      failf(data, "Socket is nonblocking and a previous connection attempt not completed: %d",errno);
      break;
    case EAGAIN:
      failf(data, "No more free local ports: %d",errno);
      break;
    case EACCES:
    case EPERM:
      failf(data, "Attempt to connect to broadcast address without socket broadcast flag or local firewall rule violated: %d",errno);
      break;
#endif
    case EINTR:
      failf(data, "Connection timed out");
      break;
    default:
      failf(data, "Can't connect to server: %d", errno);
      break;
    }
    return CURLE_COULDNT_CONNECT;
  }
#endif

  return CURLE_OK;
  return Curl_connecthost(conn,
                          conn->firstsocket, /* might be bind()ed */
                          &conn->firstsocket);
}

static CURLcode Connect(struct SessionHandle *data,
static CURLcode CreateConnection(struct SessionHandle *data,
                                 struct connectdata **in_connect,
                        bool allow_port) /* allow data->set.use_port ? */
                                 bool allow_port) /* allow set.use_port? */
{
  char *tmp;
  char *buf;
@@ -2296,7 +2197,7 @@ CURLcode Curl_connect(struct SessionHandle *data,
  struct connectdata *conn;

  /* call the stuff that needs to be called */
  code = Connect(data, in_connect, allow_port);
  code = CreateConnection(data, in_connect, allow_port);

  if(CURLE_OK != code) {
    /* We're not allowed to return failure with memory left allocated