cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: CURLOPT_LOCALPORT option broken ?!

From: <koettermarkus_at_gmx.de>
Date: Tue, 24 Feb 2009 13:41:05 +0100

Hi,

Senthil Raja Velu wrote:
> Dan, Markus,
> Ok, it works now!. Great !! thanks for your support.
> Here are the test results with the latest patch and with c-ares 1.6.0;

I've been able to verify it works on Solaris 8
SunOS 5.8 Generic_117350-44 sun4u sparc SUNW,Ultra-60 Solaris
without c-ares.

> testsvr% curl --verbose --interface bge0 --local-port 60027-60028

Curl on Solaris does not support --interface with a real interface, such
as bge0.

> https://10.20.4.19/cgi-bin/TEST/TEST.py
> * About to connect() to 10.20.4.19 port 443 (#0)
> * Trying 10.20.4.19... Could not resolve host: 10.20.4.19 (Could not
> contact DNS servers)
> * Couldn't bind to ''
> * couldn't connect to host
> * Closing connection #0
> curl: (7) Could not resolve host: 10.20.4.19 (Could not contact DNS
> servers)
>
>
> I guess the absence of DNS server in our local network is the one
> causing the problem with the patches!

Why did it work with unpatched curl in the same network on the same
machine? I don't know.
I have no idea how solaris resolver behaves if there is no dns server
(reachable).

> testsvr% curl --verbose --interface testsvr --local-port 60027-60028
> https://10.20.4.19/cgi-bin/TEST/TEST.py
> * About to connect() to 10.20.4.19 port 443 (#0)
> * Trying 10.20.4.19... Local IP/Domain 'testsvr' family 2 resolved to
> '10.0.110.131' family 2
> * Local port: 60027
> * connected

Hopefully the last version of the patch is attached.
Removes socket_domain, as it is not used, fixes
> * Couldn't bind to ''
if the dev could not be recognized as interface/domain/ip, and does not
try to connect a different/non equal af.

Please retry.

This time the patch is created different:

diff -Naur curl-7.19.4-20090219.orig/lib/connect.c
curl-7.19.4-20090219/lib/connect.c >
/tmp/curl_interface_and_port_patch-hopefully_the_last.diff

MfG
Markus

--- curl-7.19.4-20090219.orig/lib/connect.c 2008-12-31 04:00:09.000000000 +0100
+++ curl-7.19.4-20090219/lib/connect.c 2009-02-24 13:20:38.000000000 +0100
@@ -277,12 +277,15 @@
                           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,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 +340,62 @@
 
       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;
+ }else
+ {
+ /*
+ * provided dev was no interface (or interfaces are not supported e.g. solaris)
+ * no ip address and no domain
+ * we fail here
+ */
+ strncpy(myhost, dev, 255);
+ goto dev_resolv_fail;
+ }
     }
 
- 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 ( 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);
+ 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 ( 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);
+ goto bind_now;
+ }
+
+dev_resolv_fail:
+ 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);
     }
 #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);
     }
 #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 +403,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 +428,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