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:
parent
794b4da840
commit
7b7db23633
5
CHANGES
5
CHANGES
@ -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
|
||||||
|
@ -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)
|
||||||
|
210
lib/connect.c
210
lib/connect.c
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user