Commit fad3288d authored by Dan Fandrich's avatar Dan Fandrich
Browse files

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.
parent 5ecff1e4
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -6,6 +6,12 @@

                                  Changelog

Daniel Fandrich (9 Oct 2008)
- 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.

Daniel Fandrich (8 Oct 2008)
- Added tests 1082 through 1085 to test symbolic --interface parameters

+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ This release includes the following changes:
 o Better detect HTTP 1.0 servers and don't do HTTP 1.1 requests on them
 o configure --disable-proxy disables proxy
 o Added CURLOPT_USERNAME and CURLOPT_PASSWORD
 o --interface now works with IPv6 connections on glibc systems

This release includes the following bugfixes:

+1 −0
Original line number Diff line number Diff line
@@ -2061,6 +2061,7 @@ AC_CHECK_FUNCS([basename \
  fork \
  geteuid \
  gethostbyaddr \
  getifaddrs \
  getpass_r \
  getppid \
  getprotobyname \
+58 −53
Original line number Diff line number Diff line
@@ -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);
      }
    }

    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,35 +384,19 @@ 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 */
    if(af == AF_INET) {
      memset(&me, 0, sizeof(struct sockaddr));
      me.sin_family = AF_INET;
      me.sin_addr.s_addr = INADDR_ANY;
@@ -415,6 +405,17 @@ static CURLcode bindlocal(struct connectdata *conn,
      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 */
    return CURLE_OK;
@@ -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;
+6 −6
Original line number Diff line number Diff line
@@ -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);
Loading