aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDan Fandrich <dan@coneharvesters.com>2008-10-09 19:23:50 +0000
committerDan Fandrich <dan@coneharvesters.com>2008-10-09 19:23:50 +0000
commitfad3288d20fba6af71fa9bdfb851fe92c3aa2f04 (patch)
tree16b44ad8c9bfa7780292a744fc43462d16e24b1e /lib
parent5ecff1e4c3f92976371634ef40072758d1a17426 (diff)
Fixed the --interface option to work with IPv6 connections on glibc
systems supporting getifaddrs(). Also fixed a problem where an IPv6 address could be chosen instead of an IPv4 one for --interface when it involved a name lookup.
Diffstat (limited to 'lib')
-rw-r--r--lib/connect.c111
-rw-r--r--lib/ftp.c12
-rw-r--r--lib/if2ip.c62
-rw-r--r--lib/if2ip.h2
4 files changed, 124 insertions, 63 deletions
diff --git a/lib/connect.c b/lib/connect.c
index 3773b8280..07d5f3887 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -284,15 +284,16 @@ int waitconnect(curl_socket_t sockfd, /* socket */
}
static CURLcode bindlocal(struct connectdata *conn,
- curl_socket_t sockfd)
+ curl_socket_t sockfd, int af)
{
-#ifdef ENABLE_IPV6
- char ipv6_addr[16];
-#endif
struct SessionHandle *data = conn->data;
struct sockaddr_in me;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 me6;
+#endif
struct sockaddr *sock = NULL; /* bind to this address */
- socklen_t socksize; /* size of the data sock points to */
+ socklen_t socksize = 0; /* size of the data sock points to */
+ struct Curl_dns_entry *h=NULL;
unsigned short port = data->set.localport; /* use this port number, 0 for
"random" */
/* how many port numbers to try to bind to, increasing one at a time */
@@ -303,20 +304,13 @@ static CURLcode bindlocal(struct connectdata *conn,
* Select device to bind socket to
*************************************************************/
if(dev && (strlen(dev)<255) ) {
- struct Curl_dns_entry *h=NULL;
char myhost[256] = "";
- in_addr_t in;
int rc;
bool was_iface = FALSE;
- int in6 = -1;
- /* First check if the given name is an IP address */
- in=inet_addr((char *) dev);
-
- if((in == CURL_INADDR_NONE) &&
- Curl_if2ip(dev, myhost, sizeof(myhost))) {
+ if(Curl_if2ip(af, dev, myhost, sizeof(myhost))) {
/*
- * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
+ * We now have the numerical IP address in the 'myhost' buffer
*/
rc = Curl_resolv(conn, myhost, 0, &h);
if(rc == CURLRESOLV_PENDING)
@@ -324,7 +318,6 @@ static CURLcode bindlocal(struct connectdata *conn,
if(h) {
was_iface = TRUE;
- Curl_resolv_unlock(data, h);
}
}
@@ -333,22 +326,30 @@ static CURLcode bindlocal(struct connectdata *conn,
* This was not an interface, resolve the name as a host name
* or IP number
*/
+
+ /*
+ * Temporarily force name resolution to use only the address type
+ * of the connection. The resolve functions should really be changed
+ * to take a type parameter instead.
+ */
+ long ipver = data->set.ip_version;
+ if (af == AF_INET)
+ data->set.ip_version = CURL_IPRESOLVE_V4;
+ else if (af == AF_INET6)
+ data->set.ip_version = CURL_IPRESOLVE_V6;
+
rc = Curl_resolv(conn, dev, 0, &h);
if(rc == CURLRESOLV_PENDING)
(void)Curl_wait_for_resolv(conn, &h);
+ data->set.ip_version = ipver;
if(h) {
- if(in == CURL_INADDR_NONE)
- /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
- Curl_printable_address(h->addr, myhost, sizeof myhost);
- else
- /* we know data->set.device is shorter than the myhost array */
- strcpy(myhost, dev);
- Curl_resolv_unlock(data, h);
+ /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
+ Curl_printable_address(h->addr, myhost, sizeof myhost);
}
}
- if(! *myhost) {
+ if(!*myhost || !h) {
/* need to fix this
h=Curl_gethost(data,
getmyhost(*myhost,sizeof(myhost)),
@@ -356,11 +357,16 @@ static CURLcode bindlocal(struct connectdata *conn,
sizeof(hostent_buf));
*/
failf(data, "Couldn't bind to '%s'", dev);
+ if(h)
+ Curl_resolv_unlock(data, h);
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
@@ -378,42 +384,37 @@ static CURLcode bindlocal(struct connectdata *conn,
dev, strlen(dev)+1) != 0) {
/* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n",
sockfd, dev, Curl_strerror(SOCKERRNO)); */
- infof(data, "SO_BINDTODEVICE %s failed\n", dev);
+ int error = ERRNO;
+ 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
-
- in=inet_addr(myhost);
-
-#ifdef ENABLE_IPV6
- in6 = Curl_inet_pton (AF_INET6, myhost, (void *)&ipv6_addr);
-#endif
- if(CURL_INADDR_NONE == in && -1 == in6) {
- failf(data,"couldn't find my own IP address (%s)", myhost);
- return CURLE_INTERFACE_FAILED;
- } /* end of inet_addr */
-
- if( h ) {
- Curl_addrinfo *addr = h->addr;
- sock = addr->ai_addr;
- socksize = addr->ai_addrlen;
- }
- else
- return CURLE_INTERFACE_FAILED;
-
}
else if(port) {
/* if a local port number is requested but no local IP, extract the
address from the socket */
- memset(&me, 0, sizeof(struct sockaddr));
- me.sin_family = AF_INET;
- me.sin_addr.s_addr = INADDR_ANY;
+ if(af == AF_INET) {
+ memset(&me, 0, sizeof(struct sockaddr));
+ me.sin_family = AF_INET;
+ me.sin_addr.s_addr = INADDR_ANY;
- sock = (struct sockaddr *)&me;
- socksize = sizeof(struct sockaddr);
+ sock = (struct sockaddr *)&me;
+ socksize = sizeof(struct sockaddr);
+ }
+#ifdef ENABLE_IPV6
+ else { /* AF_INET6 */
+ memset(&me6, 0, sizeof(struct sockaddr));
+ me6.sin6_family = AF_INET6;
+ me6.sin6_addr = in6addr_any;
+
+ sock = (struct sockaddr *)&me6;
+ socksize = sizeof(struct sockaddr);
+ }
+#endif
}
else
/* no local kind of binding was requested */
@@ -432,11 +433,11 @@ static CURLcode bindlocal(struct connectdata *conn,
if( bind(sockfd, sock, socksize) >= 0) {
/* we succeeded to bind */
struct Curl_sockaddr_storage add;
- socklen_t size;
-
- size = sizeof(add);
+ socklen_t size = sizeof(add);
if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
failf(data, "getsockname() failed");
+ if(h)
+ Curl_resolv_unlock(data, h);
return CURLE_INTERFACE_FAILED;
}
/* We re-use/clobber the port variable here below */
@@ -448,6 +449,8 @@ static CURLcode bindlocal(struct connectdata *conn,
#endif
infof(data, "Local port: %d\n", port);
conn->bits.bound = TRUE;
+ if(h)
+ Curl_resolv_unlock(data, h);
return CURLE_OK;
}
if(--portnum > 0) {
@@ -461,8 +464,10 @@ static CURLcode bindlocal(struct connectdata *conn,
data->state.os_errno = SOCKERRNO;
failf(data, "bind failure: %s",
Curl_strerror(conn, data->state.os_errno));
- return CURLE_INTERFACE_FAILED;
+ if(h)
+ Curl_resolv_unlock(data, h);
+ return CURLE_INTERFACE_FAILED;
}
/*
@@ -822,7 +827,7 @@ singleipconnect(struct connectdata *conn,
}
/* possibly bind the local end to an IP, interface or port */
- res = bindlocal(conn, sockfd);
+ res = bindlocal(conn, sockfd, addr->family);
if(res) {
sclose(sockfd); /* close socket and bail out */
return CURL_SOCKET_BAD;
diff --git a/lib/ftp.c b/lib/ftp.c
index 8409fdc15..3d116b543 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -892,7 +892,8 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
if(data->set.str[STRING_FTPPORT] &&
(strlen(data->set.str[STRING_FTPPORT]) > 1)) {
/* attempt to get the address of the given interface name */
- if(!Curl_if2ip(data->set.str[STRING_FTPPORT], hbuf, sizeof(hbuf)))
+ if(!Curl_if2ip(conn->ip_addr->ai_family, data->set.str[STRING_FTPPORT],
+ hbuf, sizeof(hbuf)))
/* not an interface, use the given string as host name instead */
host = data->set.str[STRING_FTPPORT];
else
@@ -964,8 +965,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
/* It failed. Bind the address used for the control connection instead */
sslen = sizeof(ss);
- if(getsockname(conn->sock[FIRSTSOCKET],
- (struct sockaddr *)sa, &sslen)) {
+ if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(conn, SOCKERRNO) );
sclose(portsock);
@@ -973,7 +973,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
}
/* set port number to zero to make bind() pick "any" */
- if(((struct sockaddr *)sa)->sa_family == AF_INET)
+ if(sa->sa_family == AF_INET)
((struct sockaddr_in *)sa)->sin_port=0;
else
((struct sockaddr_in6 *)sa)->sin6_port =0;
@@ -981,7 +981,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
if(sslen > (socklen_t)sizeof(ss))
sslen = sizeof(ss);
- if(bind(portsock, (struct sockaddr *)sa, sslen)) {
+ if(bind(portsock, sa, sslen)) {
failf(data, "bind failed: %s", Curl_strerror(conn, SOCKERRNO));
sclose(portsock);
return CURLE_FTP_PORT_FAILED;
@@ -1112,7 +1112,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
/* this is an IPv4 address */
addr = Curl_ip2addr(in, ftpportstr, 0);
else {
- if(Curl_if2ip(ftpportstr, myhost, sizeof(myhost))) {
+ if(Curl_if2ip(AF_INET, ftpportstr, myhost, sizeof(myhost))) {
/* The interface to IP conversion provided a dotted address */
in=inet_addr(myhost);
addr = Curl_ip2addr(in, myhost, 0);
diff --git a/lib/if2ip.c b/lib/if2ip.c
index 94a09c11e..a54050040 100644
--- a/lib/if2ip.c
+++ b/lib/if2ip.c
@@ -42,6 +42,60 @@
!defined(__AMIGA__) && !defined(__minix) && !defined(__SYMBIAN32__) && \
!defined(__WATCOMC__)
+#if defined(HAVE_GETIFADDRS)
+
+/*
+ * glibc provides getifaddrs() to provide a list of all interfaces and their
+ * addresses.
+ */
+
+#include <ifaddrs.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "inet_ntop.h"
+#include "strequal.h"
+
+char *Curl_if2ip(int af, const char *interface, char *buf, int buf_size)
+{
+ struct ifaddrs *iface, *head;
+ char *ip=NULL;
+
+ if (getifaddrs(&head) >= 0) {
+ for (iface=head; iface != NULL; iface=iface->ifa_next) {
+ if ((iface->ifa_addr->sa_family == af) &&
+ curl_strequal(iface->ifa_name, interface)) {
+ void *addr;
+ char scope[12]="";
+ if (af == AF_INET6) {
+ unsigned int scopeid;
+ addr = &((struct sockaddr_in6 *)iface->ifa_addr)->sin6_addr;
+ /* Include the scope of this interface as part of the address */
+ scopeid = ((struct sockaddr_in6 *)iface->ifa_addr)->sin6_scope_id;
+ if (scopeid)
+ snprintf(scope, sizeof(scope), "%%%u", scopeid);
+ } else
+ addr = &((struct sockaddr_in *)iface->ifa_addr)->sin_addr;
+ ip = (char *) Curl_inet_ntop(af, addr, buf, buf_size);
+ Curl_strlcat(buf, scope, buf_size);
+ break;
+ }
+ }
+ freeifaddrs(head);
+ }
+ return ip;
+}
+
+#else
+
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
@@ -83,12 +137,12 @@
#define SYS_ERROR -1
-char *Curl_if2ip(const char *interface, char *buf, int buf_size)
+char *Curl_if2ip(int af, const char *interface, char *buf, int buf_size)
{
int dummy;
char *ip=NULL;
- if(!interface)
+ if(!interface || (af != AF_INET))
return NULL;
dummy = socket(AF_INET, SOCK_STREAM, 0);
@@ -124,11 +178,13 @@ char *Curl_if2ip(const char *interface, char *buf, int buf_size)
}
return ip;
}
+#endif
/* -- end of if2ip() -- */
#else
-char *Curl_if2ip(const char *interf, char *buf, int buf_size)
+char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size)
{
+ (void) af;
(void) interf;
(void) buf;
(void) buf_size;
diff --git a/lib/if2ip.h b/lib/if2ip.h
index 4e86e2b27..6888a4cb8 100644
--- a/lib/if2ip.h
+++ b/lib/if2ip.h
@@ -24,7 +24,7 @@
***************************************************************************/
#include "setup.h"
-extern char *Curl_if2ip(const char *interf, char *buf, int buf_size);
+extern char *Curl_if2ip(int af, const char *interf, char *buf, int buf_size);
#ifdef __INTERIX
#include <sys/socket.h>