Index: lib/connect.c
===================================================================
RCS file: /cvsroot/curl/curl/lib/connect.c,v
retrieving revision 1.141
diff -u -r1.141 connect.c
--- lib/connect.c	18 Dec 2005 15:36:14 -0000	1.141
+++ lib/connect.c	27 Jan 2006 21:59:39 -0000
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2006, 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
@@ -98,6 +98,7 @@
 #include "memory.h"
 #include "select.h"
 #include "url.h" /* for Curl_safefree() */
+#include "sockaddr.h" /* required for Curl_sockaddr_storage */
 
 /* The last #include file should be: */
 #include "memdebug.h"
@@ -239,16 +240,19 @@
 static CURLcode bindlocal(struct connectdata *conn,
                           curl_socket_t sockfd)
 {
-#ifdef HAVE_INET_NTOA
-  bool bindworked = FALSE;
   struct SessionHandle *data = conn->data;
+  struct sockaddr_in me;
+  struct sockaddr *sock = NULL;  /* bind to this address */
+  socklen_t socksize; /* size of the data sock points to */
+  unsigned short port = 40000; /* use this port number, 0 for random */
+  int portnum = 1; /* how many port numbers to try to bind to, increasing
+                      one at a time */
 
   /*************************************************************
    * Select device to bind socket to
    *************************************************************/
-  if (strlen(data->set.device)<255) {
+  if (data->set.device && (strlen(data->set.device)<255) ) {
     struct Curl_dns_entry *h=NULL;
-    size_t size;
     char myhost[256] = "";
     in_addr_t in;
     int rc;
@@ -266,8 +270,10 @@
       if(rc == CURLRESOLV_PENDING)
         (void)Curl_wait_for_resolv(conn, &h);
 
-      if(h)
+      if(h) {
         was_iface = TRUE;
+        Curl_resolv_unlock(data, h);
+      }
     }
 
     if(!was_iface) {
@@ -279,9 +285,11 @@
       if(rc == CURLRESOLV_PENDING)
         (void)Curl_wait_for_resolv(conn, &h);
 
-      if(h)
+      if(h) {
         /* we know data->set.device is shorter than the myhost array */
         strcpy(myhost, data->set.device);
+        Curl_resolv_unlock(data, h);
+      }
     }
 
     if(! *myhost) {
@@ -295,7 +303,7 @@
       return CURLE_HTTP_PORT_FAILED;
     }
 
-    infof(data, "We bind local end to %s\n", myhost);
+    infof(data, "Bind local address to %s\n", myhost);
 
 #ifdef SO_BINDTODEVICE
     /* I am not sure any other OSs than Linux that provide this feature, and
@@ -323,60 +331,75 @@
 #endif
 
     in=inet_addr(myhost);
-    if (CURL_INADDR_NONE != in) {
+    if (CURL_INADDR_NONE == in) {
+      failf(data,"couldn't find my own IP address (%s)", myhost);
+      return CURLE_HTTP_PORT_FAILED;
+    } /* end of inet_addr */
 
-      if ( h ) {
-        Curl_addrinfo *addr = h->addr;
+    if ( h ) {
+      Curl_addrinfo *addr = h->addr;
+      sock = addr->ai_addr;
+      socksize = addr->ai_addrlen;
+    }
+    else
+      return CURLE_HTTP_PORT_FAILED;
 
-        Curl_resolv_unlock(data, h);
-        /* we don't need it anymore after this function has returned */
+  }
+  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( bind(sockfd, addr->ai_addr, (socklen_t)addr->ai_addrlen) >= 0) {
-          /* we succeeded to bind */
+    sock = (struct sockaddr *)&me;
+    socksize = sizeof(struct sockaddr);
+
+  }
+  else
+    /* no local kind of binding was requested */
+    return CURLE_OK;
+
+  while(portnum--) {
+
+    /* Set port number to bind to, 0 makes the system pick one */
+    if(sock->sa_family == AF_INET)
+      ((struct sockaddr_in *)sock)->sin_port = htons(port);
 #ifdef ENABLE_IPV6
-          struct sockaddr_in6 add;
-#else
-          struct sockaddr_in add;
+    else
+      ((struct sockaddr_in6 *)sock)->sin6_port = htons(port);
 #endif
 
-          bindworked = TRUE;
-
-          size = sizeof(add);
-          if(getsockname(sockfd, (struct sockaddr *) &add,
-                         (socklen_t *)&size)<0) {
-            failf(data, "getsockname() failed");
-            return CURLE_HTTP_PORT_FAILED;
-          }
-        }
-
-        if(!bindworked) {
-          data->state.os_errno = Curl_ourerrno();
-          failf(data, "bind failure: %s",
-                Curl_strerror(conn, data->state.os_errno));
-          return CURLE_HTTP_PORT_FAILED;
-        }
-
-      } /* end of if  h */
-      else {
-        failf(data,"couldn't find my own IP address (%s)", myhost);
+    if( bind(sockfd, sock, socksize) >= 0) {
+      /* we succeeded to bind */
+      struct Curl_sockaddr_storage add;
+      unsigned short port;
+      size_t size;
+
+      size = sizeof(add);
+      if(getsockname(sockfd, (struct sockaddr *) &add,
+                     (socklen_t *)&size)<0) {
+        failf(data, "getsockname() failed");
         return CURLE_HTTP_PORT_FAILED;
       }
-    } /* end of inet_addr */
-
-    else {
-      failf(data, "couldn't find my own IP address (%s)", myhost);
-      return CURLE_HTTP_PORT_FAILED;
+      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);
+      return CURLE_OK;
     }
+    infof(data, "Bind to local port %d failed, trying next\n", port);
+    port++; /* try next port */
+  }
 
-    return CURLE_OK;
-
-  } /* end of device selection support */
-#else
-  (void)conn;
-  (void)sockfd;
-#endif /* end of HAVE_INET_NTOA */
-
+  data->state.os_errno = Curl_ourerrno();
+  failf(data, "bind failure: %s",
+        Curl_strerror(conn, data->state.os_errno));
   return CURLE_HTTP_PORT_FAILED;
+
 }
 
 /*
@@ -618,6 +641,8 @@
     infof(data, "Could not set SO_NOSIGPIPE: %s\n",
           Curl_strerror(conn, Curl_ourerrno()));
 }
+#else
+#define nosigpipe(x,y)
 #endif
 
 /* singleipconnect() connects to the given IP only, and it may return without
@@ -634,6 +659,7 @@
   bool isconnected;
   struct SessionHandle *data = conn->data;
   curl_socket_t sockfd;
+  CURLcode res;
 
   sockfd = socket(ai->ai_family, conn->socktype, ai->ai_protocol);
   if (sockfd == CURL_SOCKET_BAD)
@@ -647,17 +673,13 @@
   if(data->set.tcp_nodelay)
     tcpnodelay(conn, sockfd);
 
-#ifdef SO_NOSIGPIPE
   nosigpipe(conn, sockfd);
-#endif
-  if(conn->data->set.device) {
-    /* user selected to bind the outgoing socket to a specified "device"
-       before doing connect */
-    CURLcode res = bindlocal(conn, sockfd);
-    if(res) {
-      sclose(sockfd); /* close socket and bail out */
-      return CURL_SOCKET_BAD;
-    }
+
+  /* possibly bind the local end to an IP, interface or port */
+  res = bindlocal(conn, sockfd);
+  if(res) {
+    sclose(sockfd); /* close socket and bail out */
+    return CURL_SOCKET_BAD;
   }
 
   /* set socket non-blocking */
