]> granicus.if.org Git - curl/commitdiff
- Senthil Raja Velu reported a problem when CURLOPT_INTERFACE and
authorDaniel Stenberg <daniel@haxx.se>
Fri, 27 Feb 2009 12:07:14 +0000 (12:07 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 27 Feb 2009 12:07:14 +0000 (12:07 +0000)
  CURLOPT_LOCALPORT were used together (the local port bind failed), and
  Markus Koetter provided the fix!

CHANGES
RELEASE-NOTES
lib/connect.c

diff --git a/CHANGES b/CHANGES
index 635740f3fc8dea416edc6f8cecb49a8b1708bca5..4074f10e7f3d89f23761fea79ac42e10f7bd5f23 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,11 @@
 
                                   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)
 - As Daniel Fandrich figured out, we must do the GnuTLS initing in the
   curl_global_init() function to properly maintain the performing functions
index a65fb2bee165a4172cf2218db44b2d9e6e37151d..744569586b8f922d5b8e0a6992a774239b873443 100644 (file)
@@ -45,6 +45,7 @@ This release includes the following bugfixes:
  o CURLINFO_CONTENT_LENGTH_DOWNLOAD and CURLINFO_CONTENT_LENGTH_UPLOAD are -1
    if unknown
  o Negotiate proxy authentication
+ o CURLOPT_INTERFACE and CURLOPT_LOCALPORT used together
 
 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,
  Patrick Scott, Hidemoto Nakada, Jocelyn Jaubert, Andre Guibert de Bruet,
  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)
index 47e8681c5603d9e4bf9fe4f37832c890b3ef980b..3b2b65c715f136e436e78442410fc66b0371cf86 100644 (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
  * you should have received as part of this distribution. The terms
@@ -54,8 +54,8 @@
 #include <arpa/inet.h>
 #endif
 #ifdef HAVE_STDLIB_H
-#include <stdlib.h> /* required for free() prototype, without it, this crashes */
-#endif              /* on macos 68K */
+#include <stdlib.h>
+#endif
 
 #if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
 #include <sys/filio.h>
@@ -277,12 +277,15 @@ static CURLcode bindlocal(struct connectdata *conn,
                           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 */
+
   struct Curl_dns_entry *h=NULL;
   unsigned short port = data->set.localport; /* use this port number, 0 for
                                                 "random" */
@@ -290,40 +293,64 @@ static CURLcode bindlocal(struct connectdata *conn,
   int portnum = data->set.localportrange;
   const char *dev = data->set.str[STRING_DEVICE];
   int error;
+  char myhost[256] = "";
+  int done = 0; /* -1 for error, 1 for address found */
 
   /*************************************************************
    * 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;
 
+    /* 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);
+      infof(data, "Local Interface %s is ip %s using address family %i\n",
+            dev, myhost, af);
+      done = 1;
 
-      if(h) {
-        was_iface = TRUE;
+#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.
+       *
+       *
+       * 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
     }
-
-    if(!was_iface) {
+    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 +365,65 @@ static CURLcode bindlocal(struct connectdata *conn,
 
       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, "Name '%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;
+        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;
+      }
     }
 
-    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 */
+    if(done > 0) {
+#ifdef ENABLE_IPV6
+      /* ipv6 address */
+      if ( (af == AF_INET6) &&
+           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);
       }
-    }
+      else
 #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 ( 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);
+      return CURLE_INTERFACE_FAILED;
     }
+  }
+  else {
+    /* no device was given, prepare sa to match af's needs */
 #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);
     }
+    else
 #endif
+    if ( af == AF_INET ) {
+      si4->sin_family = AF_INET;
+      si4->sin_port = htons(port);
+      sizeof_sa = sizeof(struct sockaddr_in);
+    }
   }
-  else
-    /* no local kind of binding was requested */
-    return CURLE_OK;
 
   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 +431,23 @@ static CURLcode bindlocal(struct connectdata *conn,
         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 +456,6 @@ static CURLcode bindlocal(struct connectdata *conn,
   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;
 }