diff options
Diffstat (limited to 'lib/connect.c')
-rw-r--r-- | lib/connect.c | 233 |
1 files changed, 203 insertions, 30 deletions
diff --git a/lib/connect.c b/lib/connect.c index ef891d713..a5c2ddeb4 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -41,10 +41,14 @@ #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif #endif #include <stdio.h> #include <errno.h> +#include <string.h> #ifndef TRUE #define TRUE 1 @@ -61,6 +65,7 @@ #include "urldata.h" #include "sendf.h" +#include "if2ip.h" /* The last #include file should be: */ #ifdef MALLOCDEBUG @@ -152,25 +157,189 @@ int waitconnect(int sockfd, /* socket */ return 0; } +static CURLcode bindlocal(struct connectdata *conn, + int sockfd) +{ +#if !defined(WIN32)||defined(__CYGWIN32__) + /* We don't generally like checking for OS-versions, we should make this + HAVE_XXXX based, although at the moment I don't have a decent test for + this! */ + +#ifdef HAVE_INET_NTOA + +#ifndef INADDR_NONE +#define INADDR_NONE (unsigned long) ~0 +#endif + +#ifndef ENABLE_IPV6 + struct SessionHandle *data = conn->data; + + /************************************************************* + * Select device to bind socket to + *************************************************************/ + if (strlen(data->set.device)<255) { + struct sockaddr_in sa; + struct hostent *h=NULL; + char *hostdataptr=NULL; + size_t size; + char myhost[256] = ""; + unsigned long in; + + if(Curl_if2ip(data->set.device, myhost, sizeof(myhost))) { + h = Curl_getaddrinfo(data, myhost, 0, &hostdataptr); + } + else { + if(strlen(data->set.device)>1) { + h = Curl_getaddrinfo(data, data->set.device, 0, &hostdataptr); + } + if(h) { + /* we know data->set.device is shorter than the myhost array */ + strcpy(myhost, data->set.device); + } + } + + if(! *myhost) { + /* need to fix this + h=Curl_gethost(data, + getmyhost(*myhost,sizeof(myhost)), + hostent_buf, + sizeof(hostent_buf)); + */ + if(hostdataptr) + free(hostdataptr); /* allocated by Curl_getaddrinfo() */ + return CURLE_HTTP_PORT_FAILED; + } + + infof(data, "We bind local end to %s\n", myhost); + + if ( (in=inet_addr(myhost)) != INADDR_NONE ) { + + if ( h ) { + memset((char *)&sa, 0, sizeof(sa)); + memcpy((char *)&sa.sin_addr, + h->h_addr, + h->h_length); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = in; + sa.sin_port = 0; /* get any port */ + + if( bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) >= 0) { + /* we succeeded to bind */ + struct sockaddr_in add; + + size = sizeof(add); + if(getsockname(sockfd, (struct sockaddr *) &add, + (socklen_t *)&size)<0) { + failf(data, "getsockname() failed"); + return CURLE_HTTP_PORT_FAILED; + } + } + else { + switch(errno) { + case EBADF: + failf(data, "Invalid descriptor: %d", errno); + break; + case EINVAL: + failf(data, "Invalid request: %d", errno); + break; + case EACCES: + failf(data, "Address is protected, user not superuser: %d", errno); + break; + case ENOTSOCK: + failf(data, + "Argument is a descriptor for a file, not a socket: %d", + errno); + break; + case EFAULT: + failf(data, "Inaccessable memory error: %d", errno); + break; + case ENAMETOOLONG: + failf(data, "Address too long: %d", errno); + break; + case ENOMEM: + failf(data, "Insufficient kernel memory was available: %d", errno); + break; + default: + failf(data,"errno %d\n"); + } /* end of switch */ + + return CURLE_HTTP_PORT_FAILED; + } /* end of else */ + + } /* end of if h */ + else { + failf(data,"could't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } + } /* end of inet_addr */ + + else { + failf(data, "could't find my own IP address (%s)", myhost); + return CURLE_HTTP_PORT_FAILED; + } + + if(hostdataptr) + free(hostdataptr); /* allocated by Curl_getaddrinfo() */ + + return CURLE_OK; + + } /* end of device selection support */ +#endif /* end of HAVE_INET_NTOA */ +#endif /* end of not WIN32 */ +#endif /*ENABLE_IPV6*/ + + return CURLE_HTTP_PORT_FAILED; +} + /* * 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) +CURLcode Curl_connecthost(struct connectdata *conn, /* context */ + Curl_addrinfo *remotehost, /* use one in here */ + int port, /* connect to this */ + int *socket, /* the connected socket */ + Curl_ipconnect **addr) /* the one we used */ { struct SessionHandle *data = conn->data; int rc; + int sockfd; + int aliasindex=0; struct timeval after; struct timeval before = Curl_tvnow(); + /************************************************************* + * Figure out what maximum time we have left + *************************************************************/ + long timeout_ms=300000; /* milliseconds, default to five minutes */ + if(data->set.timeout || data->set.connecttimeout) { + double has_passed; + + /* Evaluate how much that that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start); + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + + /* get the most strict timeout of the ones converted to milliseconds */ + if(data->set.timeout && + (data->set.timeout>data->set.connecttimeout)) + timeout_ms = data->set.timeout*1000; + else + timeout_ms = data->set.connecttimeout*1000; + + /* subtract the passed time */ + timeout_ms -= (long)(has_passed * 1000); + + if(timeout_ms < 0) + /* a precaution, no need to continue if time already is up */ + return CURLE_OPERATION_TIMEOUTED; + } + #ifdef ENABLE_IPV6 struct addrinfo *ai; /* @@ -178,11 +347,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, */ 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) { + for (ai = remotehost; ai; ai = ai->ai_next, aliasindex++) { sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd < 0) continue; @@ -216,26 +381,33 @@ CURLcode Curl_connecthost(struct connectdata *conn, nonblock(sockfd, FALSE); break; } - conn->ai = ai; if (sockfd < 0) { failf(data, strerror(errno)); return CURLE_COULDNT_CONNECT; } + + if(addr) + *addr = ai; /* the address we ended up connected to */ + #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); + /* create an IPv4 TCP socket */ + sockfd = socket(AF_INET, SOCK_STREAM, 0); if(-1 == sockfd) return CURLE_COULDNT_CONNECT; /* big time error */ + + if(conn->data->set.device) { + /* user selected to bind the outgoing socket to a specified "device" + before doing connect */ + CURLcode res = bindlocal(conn, sockfd); + if(res) + return res; + } - /* non-block socket */ + /* Convert socket to non-blocking type */ nonblock(sockfd, TRUE); /* This is the loop that attempts to connect to all IP-addresses we @@ -243,9 +415,9 @@ CURLcode Curl_connecthost(struct connectdata *conn, for(rc=-1, aliasindex=0; rc && (struct in_addr *)remotehost->h_addr_list[aliasindex]; aliasindex++) { + struct sockaddr_in serv_addr; - /* copy this particular name info to the conn struct as it might - be used later in the krb4 "system" */ + /* do this nasty work to do the connect */ memset((char *) &serv_addr, '\0', sizeof(serv_addr)); memcpy((char *)&(serv_addr.sin_addr), (struct in_addr *)remotehost->h_addr_list[aliasindex], @@ -279,6 +451,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, break; default: /* unknown error, fallthrough and try another address! */ + failf(data, "Failed to connect to IP number %d", aliasindex+1); break; } } @@ -287,33 +460,33 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* get a new timeout for next attempt */ after = Curl_tvnow(); timeout_ms -= (long)(Curl_tvdiff(after, before)*1000); - if(timeout_ms < 0) + if(timeout_ms < 0) { + failf(data, "Connect timeout on IP number %d", aliasindex+1); break; + } 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); + if(addr) + /* this is the address we've connected to */ + *addr = (struct in_addr *)remotehost->h_addr_list[aliasindex]; #endif - *socket = sockfd; /* pass this to our parent */ + /* allow NULL-pointers to get passed in */ + if(socket) + *socket = sockfd; /* the socket descriptor we've connected */ return CURLE_OK; } |