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 #endif +#ifdef HAVE_ARPA_INET_H +#include +#endif #endif #include #include +#include #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; } diff --git a/lib/connect.h b/lib/connect.h index a0e3f068d..1b7319d1e 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -24,9 +24,9 @@ *****************************************************************************/ CURLcode Curl_connecthost(struct connectdata *conn, - long timeout, /* milliseconds */ Curl_addrinfo *host, /* connect to this */ long port, /* connect to this port number */ - int sockfd, /* input socket, or -1 if none */ - int *socket); /* not set if error is returned */ + int *socket, /* not set if error is returned */ + Curl_ipconnect **addr /* the one we used */ + ); /* index we used */ #endif diff --git a/lib/ftp.c b/lib/ftp.c index d887607ba..4fde25059 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -82,6 +82,7 @@ #include "strequal.h" #include "ssluse.h" +#include "connect.h" #define _MPRINTF_REPLACE /* use our functions only */ #include @@ -739,7 +740,7 @@ CURLcode ftp_getsize(struct connectdata *conn, char *file, */ static void ftp_pasv_verbose(struct connectdata *conn, - Curl_addrinfo *addr, + Curl_ipconnect *addr, char *newhost, /* ascii version */ int port) { @@ -805,7 +806,7 @@ ftp_pasv_verbose(struct connectdata *conn, #else answer = NULL; #endif - (void) memcpy(&in.s_addr, *addr->h_addr_list, sizeof (in.s_addr)); + (void) memcpy(&in.s_addr, addr, sizeof (Curl_ipconnect)); infof(conn->data, "Connecting to %s (%s) port %u\n", answer?answer->h_name:newhost, #if defined(HAVE_INET_NTOA_R) @@ -1223,12 +1224,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn) Curl_addrinfo *addr; char *hostdataptr=NULL; - -#ifdef ENABLE_IPV6 - struct addrinfo *ai; -#else - struct sockaddr_in serv_addr; -#endif + Curl_ipconnect *conninfo; char *str=buf; /* @@ -1277,72 +1273,22 @@ CURLcode ftp_use_pasv(struct connectdata *conn) connectport = newport; /* we connect to the remote port */ } -#ifdef ENABLE_IPV6 - conn->secondarysocket = -1; - for (ai = addr; ai; ai = ai->ai_next) { - /* XXX for now, we can do IPv4 only */ - if (ai->ai_family != AF_INET) - continue; - - conn->secondarysocket = socket(ai->ai_family, ai->ai_socktype, - ai->ai_protocol); - if (conn->secondarysocket < 0) - continue; - - if (connect(conn->secondarysocket, ai->ai_addr, ai->ai_addrlen) < 0) { - close(conn->secondarysocket); - conn->secondarysocket = -1; - continue; - } - - if(data->set.verbose) - /* this just dumps information about this second connection */ - ftp_pasv_verbose(conn, ai, newhost, 0 /* port not really known */); - break; - } - - if (conn->secondarysocket < 0) { - failf(data, strerror(errno)); - return CURLE_FTP_CANT_RECONNECT; - } -#else - /* IPv4 code */ - conn->secondarysocket = socket(AF_INET, SOCK_STREAM, 0); - - memset((char *) &serv_addr, '\0', sizeof(serv_addr)); - memcpy((char *)&(serv_addr.sin_addr), addr->h_addr, addr->h_length); - serv_addr.sin_family = addr->h_addrtype; - - serv_addr.sin_port = htons(connectport); - - if(data->set.verbose) + result = Curl_connecthost(conn, + addr, + connectport, + &conn->secondarysocket, + &conninfo); + + if((CURLE_OK == result) && + data->set.verbose) /* this just dumps information about this second connection */ - ftp_pasv_verbose(conn, addr, newhost, connectport); + ftp_pasv_verbose(conn, conninfo, newhost, connectport); if(hostdataptr) free(hostdataptr); - - if (connect(conn->secondarysocket, (struct sockaddr *) &serv_addr, - sizeof(serv_addr)) < 0) { - switch(errno) { -#ifdef ECONNREFUSED - /* this should be made nicer */ - case ECONNREFUSED: - failf(data, "Connection refused by ftp server"); - break; -#endif -#ifdef EINTR - case EINTR: - failf(data, "Connection timed out to ftp server"); - break; -#endif - default: - failf(data, "Can't connect to ftp server"); - break; - } - return CURLE_FTP_CANT_RECONNECT; - } -#endif /* end of IPv4-specific code*/ + + if(CURLE_OK != result) + return result; if (data->set.tunnel_thru_httpproxy) { /* We want "seamless" FTP operations through HTTP proxy tunnel */ diff --git a/lib/hostip.h b/lib/hostip.h index e05e3d5e2..86272b30e 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -27,12 +27,6 @@ struct addrinfo; struct hostent; struct SessionHandle; -#ifdef ENABLE_IPV6 -typedef struct addrinfo Curl_addrinfo; -#else -typedef struct hostent Curl_addrinfo; -#endif - Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, char *hostname, int port, diff --git a/lib/setup.h b/lib/setup.h index b5fd9f505..d238d70cc 100644 --- a/lib/setup.h +++ b/lib/setup.h @@ -144,4 +144,19 @@ int fileno( FILE *stream); #endif +/* + * Curl_addrinfo MUST be used for name resolving information. + * Information regarding a single IP witin a Curl_addrinfo MUST be stored in + * a Curl_ipconnect struct. + */ +#ifdef ENABLE_IPV6 +typedef struct addrinfo Curl_addrinfo; +typedef struct addrinfo Curl_ipconnect; +#else +typedef struct hostent Curl_addrinfo; +typedef struct in_addr Curl_ipconnect; +#endif + + + #endif /* __CONFIG_H */ diff --git a/lib/url.c b/lib/url.c index f721d5051..bbd40e827 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1113,172 +1113,35 @@ ConnectionStore(struct SessionHandle *data, return i; } -static CURLcode ConnectPlease(struct SessionHandle *data, - struct connectdata *conn) +static CURLcode ConnectPlease(struct connectdata *conn) { - long max_time=300000; /* milliseconds, default to five minutes */ - -#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 - conn->firstsocket = socket(AF_INET, SOCK_STREAM, 0); - - /************************************************************* - * Select device to bind socket to - *************************************************************/ - if (data->set.device && (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)); - */ - printf("in here\n"); - } - - infof(data, "We connect from %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(conn->firstsocket, (struct sockaddr *)&sa, sizeof(sa)) >= 0) { - /* we succeeded to bind */ - struct sockaddr_in add; - - size = sizeof(add); - if(getsockname(conn->firstsocket, (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_gethost() */ - - } /* end of device selection support */ -#endif /* end of HAVE_INET_NTOA */ -#endif /* end of not WIN32 */ -#endif /*ENABLE_IPV6*/ - - /************************************************************* - * Figure out what maximum time we have left - *************************************************************/ - 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)) - max_time = data->set.timeout*1000; - else - max_time = data->set.connecttimeout*1000; - - /* subtract the passed time */ - max_time -= (long)(has_passed * 1000); - - if(max_time < 0) - /* a precaution, no need to continue if time already is up */ - return CURLE_OPERATION_TIMEOUTED; - } + CURLcode result; + Curl_ipconnect *addr; /************************************************************* * Connect to server/proxy *************************************************************/ - return Curl_connecthost(conn, - max_time, - conn->hostaddr, - conn->port, - conn->firstsocket, /* might be bind()ed */ - &conn->firstsocket); + result= Curl_connecthost(conn, + conn->hostaddr, + conn->port, + &conn->firstsocket, + &addr); + if(CURLE_OK == result) { + /* All is cool, then we store the current information from the hostaddr + struct to the serv_addr, as it might be needed later. The address + returned from the function above is crucial here. */ +#ifdef ENABLE_IPV6 + conn->serv_addr = addr; +#else + memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr)); + memcpy((char *)&(conn->serv_addr.sin_addr), + (struct in_addr *)addr, sizeof(struct in_addr)); + conn->serv_addr.sin_family = conn->hostaddr->h_addrtype; + conn->serv_addr.sin_port = htons(conn->port); +#endif + } + + return result; } static CURLcode CreateConnection(struct SessionHandle *data, @@ -1904,16 +1767,13 @@ static CURLcode CreateConnection(struct SessionHandle *data, * port number of various reasons. * * To be able to detect port number flawlessly, we must not confuse them - * IPv6-specified addresses in the [0::1] style. + * IPv6-specified addresses in the [0::1] style. (RFC2732) *************************************************************/ if((1 == sscanf(conn->name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) && (']' == endbracket)) { - /* this is a IPv6-style specified IP-address */ -#ifndef ENABLE_IPV6 - failf(data, "You haven't enabled IPv6 support"); - return CURLE_URL_MALFORMAT; -#else + /* This is a (IPv6-style) specified IP-address. We support _any_ + IP within brackets to be really generic. */ conn->name++; /* pass the starting bracket */ tmp = strchr(conn->name, ']'); @@ -1922,7 +1782,6 @@ static CURLcode CreateConnection(struct SessionHandle *data, tmp++; /* pass the ending bracket */ if(':' != *tmp) tmp = NULL; /* no port number available */ -#endif } else { /* traditional IPv4-style port-extracting */ @@ -2148,7 +2007,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, if(-1 == conn->firstsocket) { /* Connect only if not already connected! */ - result = ConnectPlease(data, conn); + result = ConnectPlease(conn); if(CURLE_OK != result) return result; @@ -2196,8 +2055,9 @@ static CURLcode CreateConnection(struct SessionHandle *data, #else { struct in_addr in; - (void) memcpy(&in.s_addr, *conn->hostaddr->h_addr_list, sizeof (in.s_addr)); - infof(data, "Connected to %s (%s)\n", conn->hostaddr->h_name, inet_ntoa(in)); + (void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr)); + infof(data, "Connected to %s (%s)\n", conn->hostaddr->h_name, + inet_ntoa(in)); } #endif diff --git a/lib/urldata.h b/lib/urldata.h index bd756efb0..0ac3a4907 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -228,7 +228,7 @@ struct connectdata { char *hostent_buf; /* pointer to allocated memory for name info */ #ifdef ENABLE_IPV6 - struct addrinfo *ai; /* the particular host we use */ + struct addrinfo *serv_addr; /* the particular host we use */ #else struct sockaddr_in serv_addr; #endif