1
0
mirror of https://github.com/moparisthebest/curl synced 2024-11-11 20:15:03 -05:00

- Senthil Raja Velu reported a problem when CURLOPT_INTERFACE and

CURLOPT_LOCALPORT were used together (the local port bind failed), and
  Markus Koetter provided the fix!
This commit is contained in:
Daniel Stenberg 2009-02-27 12:07:14 +00:00
parent 794b4da840
commit 7b7db23633
3 changed files with 110 additions and 109 deletions

View File

@ -6,6 +6,11 @@
Changelog Changelog
Daniel Stenberg (27 Feb 2009)
- Senthil Raja Velu reported a problem when CURLOPT_INTERFACE and
CURLOPT_LOCALPORT were used together (the local port bind failed), and
Markus Koetter provided the fix!
Daniel Stenberg (25 Feb 2009) Daniel Stenberg (25 Feb 2009)
- As Daniel Fandrich figured out, we must do the GnuTLS initing in the - As Daniel Fandrich figured out, we must do the GnuTLS initing in the
curl_global_init() function to properly maintain the performing functions curl_global_init() function to properly maintain the performing functions

View File

@ -45,6 +45,7 @@ This release includes the following bugfixes:
o CURLINFO_CONTENT_LENGTH_DOWNLOAD and CURLINFO_CONTENT_LENGTH_UPLOAD are -1 o CURLINFO_CONTENT_LENGTH_DOWNLOAD and CURLINFO_CONTENT_LENGTH_UPLOAD are -1
if unknown if unknown
o Negotiate proxy authentication o Negotiate proxy authentication
o CURLOPT_INTERFACE and CURLOPT_LOCALPORT used together
This release includes the following known bugs: This release includes the following known bugs:
@ -57,6 +58,7 @@ advice from friends like these:
Peter Sylvester, Chad Monroe, Markus Moeller, Yang Tse, Scott Cantor, Peter Sylvester, Chad Monroe, Markus Moeller, Yang Tse, Scott Cantor,
Patrick Scott, Hidemoto Nakada, Jocelyn Jaubert, Andre Guibert de Bruet, Patrick Scott, Hidemoto Nakada, Jocelyn Jaubert, Andre Guibert de Bruet,
Kamil Dudka, Patrik Thunstrom, Linus Nielsen Feltzing, Mark Incley, Kamil Dudka, Patrik Thunstrom, Linus Nielsen Feltzing, Mark Incley,
Daniel Johnson, James Cheng, Brian J. Murrell Daniel Johnson, James Cheng, Brian J. Murrell, Senthil Raja Velu,
Markus Koetter
Thanks! (and sorry if I forgot to mention someone) Thanks! (and sorry if I forgot to mention someone)

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2009, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -54,8 +54,8 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#ifdef HAVE_STDLIB_H #ifdef HAVE_STDLIB_H
#include <stdlib.h> /* required for free() prototype, without it, this crashes */ #include <stdlib.h>
#endif /* on macos 68K */ #endif
#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE)) #if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
#include <sys/filio.h> #include <sys/filio.h>
@ -277,12 +277,15 @@ static CURLcode bindlocal(struct connectdata *conn,
curl_socket_t sockfd, int af) curl_socket_t sockfd, int af)
{ {
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
struct sockaddr_in me;
struct Curl_sockaddr_storage sa;
struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
socklen_t sizeof_sa = 0; /* size of the data sock points to */
struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
struct sockaddr_in6 me6; struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
#endif #endif
struct sockaddr *sock = NULL; /* bind to this address */
socklen_t socksize = 0; /* size of the data sock points to */
struct Curl_dns_entry *h=NULL; struct Curl_dns_entry *h=NULL;
unsigned short port = data->set.localport; /* use this port number, 0 for unsigned short port = data->set.localport; /* use this port number, 0 for
"random" */ "random" */
@ -290,40 +293,64 @@ static CURLcode bindlocal(struct connectdata *conn,
int portnum = data->set.localportrange; int portnum = data->set.localportrange;
const char *dev = data->set.str[STRING_DEVICE]; const char *dev = data->set.str[STRING_DEVICE];
int error; int error;
char myhost[256] = "";
int done = 0; /* -1 for error, 1 for address found */
/************************************************************* /*************************************************************
* Select device to bind socket to * Select device to bind socket to
*************************************************************/ *************************************************************/
if(dev && (strlen(dev)<255) ) { if ( !dev && !port )
char myhost[256] = ""; /* no local kind of binding was requested */
int rc; return CURLE_OK;
bool was_iface = FALSE;
memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
if(dev && (strlen(dev)<255) ) {
/* interface */
if(Curl_if2ip(af, dev, myhost, sizeof(myhost))) { if(Curl_if2ip(af, dev, myhost, sizeof(myhost))) {
/* /*
* We now have the numerical IP address in the 'myhost' buffer * We now have the numerical IP address in the 'myhost' buffer
*/ */
rc = Curl_resolv(conn, myhost, 0, &h); infof(data, "Local Interface %s is ip %s using address family %i\n",
if(rc == CURLRESOLV_PENDING) dev, myhost, af);
(void)Curl_wait_for_resolv(conn, &h); done = 1;
if(h) { #ifdef SO_BINDTODEVICE
was_iface = TRUE; /* I am not sure any other OSs than Linux that provide this feature, and
* at the least I cannot test. --Ben
*
* This feature allows one to tightly bind the local socket to a
* particular interface. This will force even requests to other local
* interfaces to go out the external interface.
*
*
* Only bind to the interface when specified as interface, not just as a
* hostname or ip address.
*/
if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
dev, strlen(dev)+1) != 0) {
error = SOCKERRNO;
infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;"
" will do regular bind\n",
dev, error, Curl_strerror(conn, error));
/* This is typically "errno 1, error: Operation not permitted" if
you're not running as root or another suitable privileged user */
} }
#endif
} }
else {
if(!was_iface) {
/* /*
* This was not an interface, resolve the name as a host name * This was not an interface, resolve the name as a host name
* or IP number * or IP number
*/ *
/*
* Temporarily force name resolution to use only the address type * Temporarily force name resolution to use only the address type
* of the connection. The resolve functions should really be changed * of the connection. The resolve functions should really be changed
* to take a type parameter instead. * to take a type parameter instead.
*/ */
long ipver = data->set.ip_version; long ipver = data->set.ip_version;
int rc;
if (af == AF_INET) if (af == AF_INET)
data->set.ip_version = CURL_IPRESOLVE_V4; data->set.ip_version = CURL_IPRESOLVE_V4;
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
@ -338,92 +365,64 @@ static CURLcode bindlocal(struct connectdata *conn,
if(h) { if(h) {
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
Curl_printable_address(h->addr, myhost, sizeof myhost); Curl_printable_address(h->addr, myhost, sizeof(myhost));
infof(data, "Name '%s' family %i resolved to '%s' family %i\n",
dev, af, myhost, h->addr->ai_family);
Curl_resolv_unlock(data, h);
done = 1;
}
else {
/*
* provided dev was no interface (or interfaces are not supported
* e.g. solaris) no ip address and no domain we fail here
*/
done = -1;
} }
} }
if(!*myhost || !h) { if(done > 0) {
/* need to fix this #ifdef ENABLE_IPV6
h=Curl_gethost(data, /* ipv6 address */
getmyhost(*myhost,sizeof(myhost)), if ( (af == AF_INET6) &&
hostent_buf, inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0 ) {
sizeof(hostent_buf)); si6->sin6_family = AF_INET6;
*/ si6->sin6_port = htons(port);
sizeof_sa = sizeof(struct sockaddr_in6);
}
else
#endif
/* ipv4 address */
if ( af == AF_INET && inet_pton(AF_INET, myhost, &si4->sin_addr) > 0 ) {
si4->sin_family = AF_INET;
si4->sin_port = htons(port);
sizeof_sa = sizeof(struct sockaddr_in);
}
}
if(done < 1) {
failf(data, "Couldn't bind to '%s'", dev); failf(data, "Couldn't bind to '%s'", dev);
if(h)
Curl_resolv_unlock(data, h);
return CURLE_INTERFACE_FAILED; return CURLE_INTERFACE_FAILED;
} }
infof(data, "Bind local address to %s\n", myhost);
sock = h->addr->ai_addr;
socksize = h->addr->ai_addrlen;
#ifdef SO_BINDTODEVICE
/* I am not sure any other OSs than Linux that provide this feature, and
* at the least I cannot test. --Ben
*
* This feature allows one to tightly bind the local socket to a
* particular interface. This will force even requests to other local
* interfaces to go out the external interface.
*
*/
if(was_iface) {
/* Only bind to the interface when specified as interface, not just as a
* hostname or ip address.
*/
if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
dev, strlen(dev)+1) != 0) {
error = SOCKERRNO;
infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s; will do regular bind\n",
dev, error, Curl_strerror(conn, error));
/* This is typically "errno 1, error: Operation not permitted" if
you're not running as root or another suitable privileged user */
}
}
#endif
}
else if(port) {
/* if a local port number is requested but no local IP, extract the
address from the socket */
if(af == AF_INET) {
memset(&me, 0, sizeof(me));
me.sin_family = AF_INET;
me.sin_addr.s_addr = INADDR_ANY;
sock = (struct sockaddr *)&me;
socksize = sizeof(me);
} }
else {
/* no device was given, prepare sa to match af's needs */
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
else { /* AF_INET6 */ if ( af == AF_INET6 ) {
memset(&me6, 0, sizeof(me6)); si6->sin6_family = AF_INET6;
me6.sin6_family = AF_INET6; si6->sin6_port = htons(port);
/* in6addr_any isn't always available and since me6 has just been sizeof_sa = sizeof(struct sockaddr_in6);
cleared, it's not strictly necessary to use it here */
/*me6.sin6_addr = in6addr_any;*/
sock = (struct sockaddr *)&me6;
socksize = sizeof(me6);
}
#endif
} }
else else
/* no local kind of binding was requested */ #endif
return CURLE_OK; if ( af == AF_INET ) {
si4->sin_family = AF_INET;
si4->sin_port = htons(port);
sizeof_sa = sizeof(struct sockaddr_in);
}
}
do { do {
if( bind(sockfd, sock, sizeof_sa) >= 0) {
/* Set port number to bind to, 0 makes the system pick one */
if(sock->sa_family == AF_INET)
me.sin_port = htons(port);
#ifdef ENABLE_IPV6
else
me6.sin6_port = htons(port);
#endif
if( bind(sockfd, sock, socksize) >= 0) {
/* we succeeded to bind */ /* we succeeded to bind */
struct Curl_sockaddr_storage add; struct Curl_sockaddr_storage add;
socklen_t size = sizeof(add); socklen_t size = sizeof(add);
@ -432,26 +431,23 @@ static CURLcode bindlocal(struct connectdata *conn,
data->state.os_errno = error = SOCKERRNO; data->state.os_errno = error = SOCKERRNO;
failf(data, "getsockname() failed with errno %d: %s", failf(data, "getsockname() failed with errno %d: %s",
error, Curl_strerror(conn, error)); error, Curl_strerror(conn, error));
if(h)
Curl_resolv_unlock(data, h);
return CURLE_INTERFACE_FAILED; return CURLE_INTERFACE_FAILED;
} }
/* We re-use/clobber the port variable here below */
if(((struct sockaddr *)&add)->sa_family == AF_INET)
port = ntohs(((struct sockaddr_in *)&add)->sin_port);
#ifdef ENABLE_IPV6
else
port = ntohs(((struct sockaddr_in6 *)&add)->sin6_port);
#endif
infof(data, "Local port: %d\n", port); infof(data, "Local port: %d\n", port);
conn->bits.bound = TRUE; conn->bits.bound = TRUE;
if(h)
Curl_resolv_unlock(data, h);
return CURLE_OK; return CURLE_OK;
} }
if(--portnum > 0) { if(--portnum > 0) {
infof(data, "Bind to local port %d failed, trying next\n", port); infof(data, "Bind to local port %d failed, trying next\n", port);
port++; /* try next port */ port++; /* try next port */
/* We re-use/clobber the port variable here below */
if(sock->sa_family == AF_INET)
si4->sin_port = ntohs(port);
#ifdef ENABLE_IPV6
else
si6->sin6_port = ntohs(port);
#endif
} }
else else
break; break;
@ -460,8 +456,6 @@ static CURLcode bindlocal(struct connectdata *conn,
data->state.os_errno = error = SOCKERRNO; data->state.os_errno = error = SOCKERRNO;
failf(data, "bind failed with errno %d: %s", failf(data, "bind failed with errno %d: %s",
error, Curl_strerror(conn, error)); error, Curl_strerror(conn, error));
if(h)
Curl_resolv_unlock(data, h);
return CURLE_INTERFACE_FAILED; return CURLE_INTERFACE_FAILED;
} }