cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: CURLOPT_LOCALPORT option broken ?!

From: <koettermarkus_at_gmx.de>
Date: Tue, 24 Feb 2009 04:50:43 +0100

Hi,

attached is a new version of the patch, I removed the SO_BINDDEVICE code
, as it is not possible to bind an outgoing connection to an interface.
SO_BINDTODEVICE is meant to serve for bind()&listen() only.

Subject: Behavior of SO_BINDTODEVICE
Date: Tuesday, November 11, 2008 - 6:09 am

https://kerneltrap.org/mailarchive/linux-net/2008/11/11/4058724/thread
( if you get ssl errors,
http://209.85.129.132/search?q=cache:ufmrJgCzFe0J:https://kerneltrap.org/mailarchive/linux-net/2008/11/11/4058724/thread
)

"Well, first of all, the BINDTODEVICE is not intended to regulate the
source of response IP Addresses. It only regulates which Incoming TCP
Connections are accepted. IP Communication still is processed by the IP
FIB (Cache)." - Bernd Eckenfels

Additionally, I improved the quality of the debug strings, hopefully
this will help nailing the solaris issue down.

MfG
Markus

--- lib/connect.c.orig 2009-02-19 23:50:15.000000000 +0100
+++ lib/connect.c 2009-02-24 04:38:53.000000000 +0100
@@ -18,7 +18,7 @@
  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  * KIND, either express or implied.
  *
- * $Id: connect.c,v 1.211 2008/12/30 08:05:38 gknauf Exp $
+ * $Id: connect.c,v 1.211 2008-12-30 08:05:38 gknauf Exp $
  ***************************************************************************/
 
 #include "setup.h"
@@ -277,12 +277,16 @@
                           curl_socket_t sockfd, int af)
 {
   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
- struct sockaddr_in6 me6;
+ struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
 #endif
- struct sockaddr *sock = NULL; /* bind to this address */
- socklen_t socksize = 0; /* size of the data sock points to */
+ int socket_domain = 0;
+
   struct Curl_dns_entry *h=NULL;
   unsigned short port = data->set.localport; /* use this port number, 0 for
                                                 "random" */
@@ -290,40 +294,39 @@
   int portnum = data->set.localportrange;
   const char *dev = data->set.str[STRING_DEVICE];
   int error;
+ char myhost[256] = "";
 
   /*************************************************************
    * Select device to bind socket to
    *************************************************************/
+ if ( !dev && !port )
+ /* no local kind of binding was requested */
+ return CURLE_OK;
+
+ memset(&sa,0,sizeof(struct Curl_sockaddr_storage));
+
   if(dev && (strlen(dev)<255) ) {
- char myhost[256] = "";
- int rc;
- bool was_iface = FALSE;
 
- if(Curl_if2ip(af, dev, myhost, sizeof(myhost))) {
+ /* interface */
+ if(Curl_if2ip(af, dev, myhost, sizeof(myhost)))
+ {
       /*
        * We now have the numerical IP address in the 'myhost' buffer
        */
- rc = Curl_resolv(conn, myhost, 0, &h);
- if(rc == CURLRESOLV_PENDING)
- (void)Curl_wait_for_resolv(conn, &h);
-
- if(h) {
- was_iface = TRUE;
- }
- }
-
- if(!was_iface) {
+ infof(data, "Local Interface %s is ip %s using address family %i\n", dev, myhost, af);
+ }else
+ {
       /*
        * 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;
+ int rc;
+
       if (af == AF_INET)
         data->set.ip_version = CURL_IPRESOLVE_V4;
 #ifdef ENABLE_IPV6
@@ -338,93 +341,56 @@
 
       if(h) {
         /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
- Curl_printable_address(h->addr, myhost, sizeof myhost);
- }
- }
-
- if(!*myhost || !h) {
- /* need to fix this
- h=Curl_gethost(data,
- getmyhost(*myhost,sizeof(myhost)),
- hostent_buf,
- sizeof(hostent_buf));
- */
- failf(data, "Couldn't bind to '%s'", dev);
- if(h)
+ Curl_printable_address(h->addr, myhost, sizeof(myhost));
+ infof(data, "Local IP/Domain '%s' family %i resolved to '%s' family %i\n", dev, af, myhost, h->addr->ai_family);
         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
- *
- * 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 */
- }
+#ifdef ENABLE_IPV6
+ /* ipv6 address */
+ if ( inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0 ) {
+ si6->sin6_family = AF_INET6;
+ si6->sin6_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in6);
+ socket_domain = PF_INET6;
+ goto bind_now;
     }
 #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);
 
+ /* ipv4 address */
+ if ( 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);
+ socket_domain = PF_INET;
+ goto bind_now;
+ }
+
+ failf(data, "Couldn't bind to '%s'", dev);
+ return CURLE_INTERFACE_FAILED;
+ }else
+ { /* no device was given, prepare sa to match af's needs */
+ if ( af == AF_INET ) {
+ si4->sin_family = AF_INET;
+ si4->sin_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in);
+ socket_domain = PF_INET;
     }
 #ifdef ENABLE_IPV6
- else { /* AF_INET6 */
- memset(&me6, 0, sizeof(me6));
- me6.sin6_family = AF_INET6;
- /* in6addr_any isn't always available and since me6 has just been
- cleared, it's not strictly necessary to use it here */
- /*me6.sin6_addr = in6addr_any;*/
-
- sock = (struct sockaddr *)&me6;
- socksize = sizeof(me6);
+ if ( af == AF_INET6 ) {
+ si6->sin6_family = AF_INET6;
+ si6->sin6_port = htons(port);
+ sizeof_sa = sizeof(struct sockaddr_in6);
+ socket_domain = PF_INET6;
     }
 #endif
- }
- else
- /* no local kind of binding was requested */
- return CURLE_OK;
+ }
 
+bind_now:
   do {
-
- /* 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 */
+ if( bind(sockfd, sock, sizeof_sa) >= 0) {
+ /* we succeeded to bind */
       struct Curl_sockaddr_storage add;
       socklen_t size = sizeof(add);
       memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
@@ -432,26 +398,23 @@
         data->state.os_errno = error = SOCKERRNO;
         failf(data, "getsockname() failed with errno %d: %s",
               error, Curl_strerror(conn, error));
- if(h)
- Curl_resolv_unlock(data, h);
         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);
       conn->bits.bound = TRUE;
- if(h)
- Curl_resolv_unlock(data, h);
       return CURLE_OK;
     }
+
     if(--portnum > 0) {
       infof(data, "Bind to local port %d failed, trying next\n", 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
       break;
@@ -460,8 +423,6 @@
   data->state.os_errno = error = SOCKERRNO;
   failf(data, "bind failed with errno %d: %s",
         error, Curl_strerror(conn, error));
- if(h)
- Curl_resolv_unlock(data, h);
 
   return CURLE_INTERFACE_FAILED;
 }
Received on 2009-02-24