Пример #1
0
static CURLcode bindlocal(struct connectdata *conn,
                          curl_socket_t sockfd, int af)
{
    struct SessionHandle *data = conn->data;

    struct Curl_sockaddr_storage sa;
    struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
    curl_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 *si6 = (struct sockaddr_in6 *)&sa;
#endif

    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 */
    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) ) {

        /* interface */
        if(Curl_if2ip(af, dev, myhost, sizeof(myhost))) {
            /*
             * We now have the numerical IP address in the 'myhost' buffer
             */
            infof(data, "Local Interface %s is ip %s using address family %i\n",
                  dev, myhost, af);
            done = 1;

#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, (curl_socklen_t)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
        }
        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
            else if (af == AF_INET6)
                data->set.ip_version = CURL_IPRESOLVE_V6;
#endif

            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) {
                /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
                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);
                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;
            }
        }

        if(done > 0) {
#ifdef ENABLE_IPV6
            /* ipv6 address */
            if((af == AF_INET6) &&
                    (Curl_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
                /* ipv4 address */
                if((af == AF_INET) &&
                        (Curl_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
        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);
            }
    }

    for(;;) {
        if( bind(sockfd, sock, sizeof_sa) >= 0) {
            /* we succeeded to bind */
            struct Curl_sockaddr_storage add;
            curl_socklen_t size = sizeof(add);
            memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
            if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
                data->state.os_errno = error = SOCKERRNO;
                failf(data, "getsockname() failed with errno %d: %s",
                      error, Curl_strerror(conn, error));
                return CURLE_INTERFACE_FAILED;
            }
            infof(data, "Local port: %hu\n", port);
            conn->bits.bound = TRUE;
            return CURLE_OK;
        }

        if(--portnum > 0) {
            infof(data, "Bind to local port %hu 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;
    }

    data->state.os_errno = error = SOCKERRNO;
    failf(data, "bind failed with errno %d: %s",
          error, Curl_strerror(conn, error));

    return CURLE_INTERFACE_FAILED;
}
Пример #2
0
static CURLcode bindlocal(struct connectdata *conn,
                          curl_socket_t sockfd)
{
  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 = data->set.localport; /* use this port number, 0 for
                                                "random" */
  /* how many port numbers to try to bind to, increasing one at a time */
  int portnum = data->set.localportrange;

  /*************************************************************
   * Select device to bind socket to
   *************************************************************/
  if (data->set.device && (strlen(data->set.device)<255) ) {
    struct Curl_dns_entry *h=NULL;
    char myhost[256] = "";
    in_addr_t in;
    int rc;
    bool was_iface = FALSE;

    /* First check if the given name is an IP address */
    in=inet_addr(data->set.device);

    if((in == CURL_INADDR_NONE) &&
       Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
      /*
       * We now have the numerical IPv4-style x.y.z.w 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;
        Curl_resolv_unlock(data, h);
      }
    }

    if(!was_iface) {
      /*
       * This was not an interface, resolve the name as a host name
       * or IP number
       */
      rc = Curl_resolv(conn, data->set.device, 0, &h);
      if(rc == CURLRESOLV_PENDING)
        (void)Curl_wait_for_resolv(conn, &h);

      if(h) {
        if(in == CURL_INADDR_NONE)
          /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
          Curl_inet_ntop(h->addr->ai_addr->sa_family,
                         &((struct sockaddr_in*)h->addr->ai_addr)->sin_addr,
                         myhost, sizeof myhost);
        else
          /* we know data->set.device is shorter than the myhost array */
          strcpy(myhost, data->set.device);
        Curl_resolv_unlock(data, h);
      }
    }

    if(! *myhost) {
      /* need to fix this
         h=Curl_gethost(data,
         getmyhost(*myhost,sizeof(myhost)),
         hostent_buf,
         sizeof(hostent_buf));
      */
      failf(data, "Couldn't bind to '%s'", data->set.device);
      return CURLE_HTTP_PORT_FAILED;
    }

    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
     * 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,
                     data->set.device, strlen(data->set.device)+1) != 0) {
        /* printf("Failed to BINDTODEVICE, socket: %d  device: %s error: %s\n",
           sockfd, data->set.device, Curl_strerror(Curl_sockerrno())); */
        infof(data, "SO_BINDTODEVICE %s failed\n",
              data->set.device);
        /* 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);
    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;
      sock = addr->ai_addr;
      socksize = addr->ai_addrlen;
    }
    else
      return CURLE_HTTP_PORT_FAILED;

  }
  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;

    sock = (struct sockaddr *)&me;
    socksize = sizeof(struct sockaddr);

  }
  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)
      ((struct sockaddr_in *)sock)->sin_port = htons(port);
#ifdef ENABLE_IPV6
    else
      ((struct sockaddr_in6 *)sock)->sin6_port = htons(port);
#endif

    if( bind(sockfd, sock, socksize) >= 0) {
      /* we succeeded to bind */
      struct Curl_sockaddr_storage add;
      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;
      }
      /* 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);
      return CURLE_OK;
    }
    if(--portnum > 0) {
      infof(data, "Bind to local port %d failed, trying next\n", port);
      port++; /* try next port */
    }
    else
      break;
  } while(1);

  data->state.os_errno = Curl_sockerrno();
  failf(data, "bind failure: %s",
        Curl_strerror(conn, data->state.os_errno));
  return CURLE_HTTP_PORT_FAILED;

}
Пример #3
0
/*
* This function logs in to a SOCKS4 proxy and sends the specifics to the final
* destination server.
*
* Reference :
*   http://socks.permeo.com/protocol/socks4.protocol
*
* Note :
*   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
*   Nonsupport "Identification Protocol (RFC1413)"
*/
CURLcode Curl_SOCKS4(const char *proxy_name,
                     const char *hostname,
                     int remote_port,
                     int sockindex,
                     struct connectdata *conn,
                     bool protocol4a)
{
#define SOCKS4REQLEN 262
  unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
                                           id */
  int result;
  CURLcode code;
  curl_socket_t sock = conn->sock[sockindex];
  long timeout;
  struct SessionHandle *data = conn->data;

  /* get timeout */
  timeout = Curl_timeleft(conn, NULL, TRUE);

  if(timeout < 0) {
    /* time-out, bail out, go home */
    failf(data, "Connection time-out");
    return CURLE_OPERATION_TIMEDOUT;
  }

  Curl_nonblock(sock, FALSE);

  /*
   * Compose socks4 request
   *
   * Request format
   *
   *     +----+----+----+----+----+----+----+----+----+----+....+----+
   *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
   *     +----+----+----+----+----+----+----+----+----+----+....+----+
   * # of bytes:  1    1      2              4           variable       1
   */

  socksreq[0] = 4; /* version (SOCKS4) */
  socksreq[1] = 1; /* connect */
  *((unsigned short*)&socksreq[2]) = htons((unsigned short)remote_port);

  /* DNS resolve only for SOCKS4, not SOCKS4a */
  if (!protocol4a) {
    struct Curl_dns_entry *dns;
    Curl_addrinfo *hp=NULL;
    int rc;

    rc = Curl_resolv(conn, hostname, remote_port, &dns);

    if(rc == CURLRESOLV_ERROR)
      return CURLE_COULDNT_RESOLVE_PROXY;

    if(rc == CURLRESOLV_PENDING)
      /* this requires that we're in "wait for resolve" state */
      rc = Curl_wait_for_resolv(conn, &dns);

    /*
     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
     * returns a Curl_addrinfo pointer that may not always look the same.
     */
    if(dns)
      hp=dns->addr;
    if(hp) {
      char buf[64];
      unsigned short ip[4];
      Curl_printable_address(hp, buf, sizeof(buf));

      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
                      &ip[0], &ip[1], &ip[2], &ip[3])) {
        /* Set DSTIP */
        socksreq[4] = (unsigned char)ip[0];
        socksreq[5] = (unsigned char)ip[1];
        socksreq[6] = (unsigned char)ip[2];
        socksreq[7] = (unsigned char)ip[3];
      }
      else
        hp = NULL; /* fail! */

      Curl_resolv_unlock(data, dns); /* not used anymore from now on */

    }
    if(!hp) {
      failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
            hostname);
      return CURLE_COULDNT_RESOLVE_HOST;
    }
  }

  /*
   * This is currently not supporting "Identification Protocol (RFC1413)".
   */
  socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
  if(proxy_name)
    strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);

  /*
   * Make connection
   */
  {
    ssize_t actualread;
    ssize_t written;
    ssize_t hostnamelen = 0;
    int packetsize = 9 +
      (int)strlen((char*)socksreq + 8); /* size including NUL */

    /* If SOCKS4a, set special invalid IP address 0.0.0.x */
    if (protocol4a) {
      socksreq[4] = 0;
      socksreq[5] = 0;
      socksreq[6] = 0;
      socksreq[7] = 1;
      /* If still enough room in buffer, also append hostname */
      hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
      if (packetsize + hostnamelen <= SOCKS4REQLEN)
        strcpy((char*)socksreq + packetsize, hostname);
      else
        hostnamelen = 0; /* Flag: hostname did not fit in buffer */
    }

    /* Send request */
    code = Curl_write_plain(conn, sock, (char *)socksreq,
                            packetsize + hostnamelen,
                            &written);
    if((code != CURLE_OK) || (written != packetsize + hostnamelen)) {
      failf(data, "Failed to send SOCKS4 connect request.");
      return CURLE_COULDNT_CONNECT;
    }
    if (protocol4a && hostnamelen == 0) {
      /* SOCKS4a with very long hostname - send that name separately */
      hostnamelen = (ssize_t)strlen(hostname) + 1;
      code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
                              &written);
      if((code != CURLE_OK) || (written != hostnamelen)) {
        failf(data, "Failed to send SOCKS4 connect request.");
        return CURLE_COULDNT_CONNECT;
      }
    }

    packetsize = 8; /* receive data size */

    /* Receive response */
    result = blockread_all(conn, sock, (char *)socksreq, packetsize,
                           &actualread, timeout);
    if((result != CURLE_OK) || (actualread != packetsize)) {
      failf(data, "Failed to receive SOCKS4 connect request ack.");
      return CURLE_COULDNT_CONNECT;
    }

    /*
     * Response format
     *
     *     +----+----+----+----+----+----+----+----+
     *     | VN | CD | DSTPORT |      DSTIP        |
     *     +----+----+----+----+----+----+----+----+
     * # of bytes:  1    1      2              4
     *
     * VN is the version of the reply code and should be 0. CD is the result
     * code with one of the following values:
     *
     * 90: request granted
     * 91: request rejected or failed
     * 92: request rejected because SOCKS server cannot connect to
     *     identd on the client
     * 93: request rejected because the client program and identd
     *     report different user-ids
     */

    /* wrong version ? */
    if(socksreq[0] != 0) {
      failf(data,
            "SOCKS4 reply has wrong version, version should be 4.");
      return CURLE_COULDNT_CONNECT;
    }

    /* Result */
    switch(socksreq[1])
    {
    case 90:
      if (protocol4a)
        infof(data, "SOCKS4a request granted.\n");
      else
        infof(data, "SOCKS4 request granted.\n");
      break;
    case 91:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", request rejected or failed.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    case 92:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", request rejected because SOCKS server cannot connect to "
            "identd on the client.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    case 93:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", request rejected because the client program and identd "
            "report different user-ids.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    default:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", Unknown.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    }
  }

  Curl_nonblock(sock, TRUE);

  return CURLE_OK; /* Proxy was successful! */
}
Пример #4
0
/*
 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
 * destination server.
 */
CURLcode Curl_SOCKS5(const char *proxy_name,
                     const char *proxy_password,
                     const char *hostname,
                     int remote_port,
                     int sockindex,
                     struct connectdata *conn)
{
  /*
    According to the RFC1928, section "6.  Replies". This is what a SOCK5
    replies:

        +----+-----+-------+------+----------+----------+
        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
        +----+-----+-------+------+----------+----------+
        | 1  |  1  | X'00' |  1   | Variable |    2     |
        +----+-----+-------+------+----------+----------+

    Where:

    o  VER    protocol version: X'05'
    o  REP    Reply field:
    o  X'00' succeeded
  */

  unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
  ssize_t actualread;
  ssize_t written;
  int result;
  CURLcode code;
  curl_socket_t sock = conn->sock[sockindex];
  struct SessionHandle *data = conn->data;
  long timeout;
  bool socks5_resolve_local = (bool)(data->set.proxytype == CURLPROXY_SOCKS5);
  const size_t hostname_len = strlen(hostname);
  ssize_t packetsize = 0;

  /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
  if(!socks5_resolve_local && hostname_len > 255)
  {
    infof(conn->data,"SOCKS5: server resolving disabled for hostnames of "
          "length > 255 [actual len=%d]\n", hostname_len);
    socks5_resolve_local = TRUE;
  }

  /* get timeout */
  timeout = Curl_timeleft(conn, NULL, TRUE);

  if(timeout < 0) {
    /* time-out, bail out, go home */
    failf(data, "Connection time-out");
    return CURLE_OPERATION_TIMEDOUT;
  }

  Curl_nonblock(sock, TRUE);

  /* wait until socket gets connected */
  result = Curl_socket_ready(CURL_SOCKET_BAD, sock, (int)timeout);

  if(-1 == result) {
    failf(conn->data, "SOCKS5: no connection here");
    return CURLE_COULDNT_CONNECT;
  }
  else if(0 == result) {
    failf(conn->data, "SOCKS5: connection timeout");
    return CURLE_OPERATION_TIMEDOUT;
  }

  if(result & CURL_CSELECT_ERR) {
    failf(conn->data, "SOCKS5: error occured during connection");
    return CURLE_COULDNT_CONNECT;
  }

  socksreq[0] = 5; /* version */
  socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
  socksreq[2] = 0; /* no authentication */
  socksreq[3] = 2; /* username/password */

  Curl_nonblock(sock, FALSE);

  code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
                          &written);
  if((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
    failf(data, "Unable to send initial SOCKS5 request.");
    return CURLE_COULDNT_CONNECT;
  }

  Curl_nonblock(sock, TRUE);

  result = Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)timeout);

  if(-1 == result) {
    failf(conn->data, "SOCKS5 nothing to read");
    return CURLE_COULDNT_CONNECT;
  }
  else if(0 == result) {
    failf(conn->data, "SOCKS5 read timeout");
    return CURLE_OPERATION_TIMEDOUT;
  }

  if(result & CURL_CSELECT_ERR) {
    failf(conn->data, "SOCKS5 read error occured");
    return CURLE_RECV_ERROR;
  }

  Curl_nonblock(sock, FALSE);

  result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout);
  if((result != CURLE_OK) || (actualread != 2)) {
    failf(data, "Unable to receive initial SOCKS5 response.");
    return CURLE_COULDNT_CONNECT;
  }

  if(socksreq[0] != 5) {
    failf(data, "Received invalid version in initial SOCKS5 response.");
    return CURLE_COULDNT_CONNECT;
  }
  if(socksreq[1] == 0) {
    /* Nothing to do, no authentication needed */
    ;
  }
  else if(socksreq[1] == 2) {
    /* Needs user name and password */
    size_t userlen, pwlen;
    int len;
    if(proxy_name && proxy_password) {
      userlen = strlen(proxy_name);
      pwlen = strlen(proxy_password);
    }
    else {
      userlen = 0;
      pwlen = 0;
    }

    /*   username/password request looks like
     * +----+------+----------+------+----------+
     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
     * +----+------+----------+------+----------+
     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
     * +----+------+----------+------+----------+
     */
    len = 0;
    socksreq[len++] = 1;    /* username/pw subnegotiation version */
    socksreq[len++] = (char) userlen;
    memcpy(socksreq + len, proxy_name, (int) userlen);
    len += userlen;
    socksreq[len++] = (char) pwlen;
    memcpy(socksreq + len, proxy_password, (int) pwlen);
    len += pwlen;

    code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
    if((code != CURLE_OK) || (len != written)) {
      failf(data, "Failed to send SOCKS5 sub-negotiation request.");
      return CURLE_COULDNT_CONNECT;
    }

    result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
                         timeout);
    if((result != CURLE_OK) || (actualread != 2)) {
      failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
      return CURLE_COULDNT_CONNECT;
    }

    /* ignore the first (VER) byte */
    if(socksreq[1] != 0) { /* status */
      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
            socksreq[0], socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    }

    /* Everything is good so far, user was authenticated! */
  }
  else {
    /* error */
    if(socksreq[1] == 1) {
      failf(data,
            "SOCKS5 GSSAPI per-message authentication is not supported.");
      return CURLE_COULDNT_CONNECT;
    }
    else if(socksreq[1] == 255) {
      if(!proxy_name || !*proxy_name) {
        failf(data,
              "No authentication method was acceptable. (It is quite likely"
              " that the SOCKS5 server wanted a username/password, since none"
              " was supplied to the server on this connection.)");
      }
      else {
        failf(data, "No authentication method was acceptable.");
      }
      return CURLE_COULDNT_CONNECT;
    }
    else {
      failf(data,
            "Undocumented SOCKS5 mode attempted to be used by server.");
      return CURLE_COULDNT_CONNECT;
    }
  }

  /* Authentication is complete, now specify destination to the proxy */
  socksreq[0] = 5; /* version (SOCKS5) */
  socksreq[1] = 1; /* connect */
  socksreq[2] = 0; /* must be zero */

  if(!socks5_resolve_local) {
    packetsize = (ssize_t)(5 + hostname_len + 2);

    socksreq[3] = 3; /* ATYP: domain name = 3 */
    socksreq[4] = (char) hostname_len; /* address length */
    memcpy(&socksreq[5], hostname, hostname_len); /* address bytes w/o NULL */

    *((unsigned short*)&socksreq[hostname_len+5]) =
      htons((unsigned short)remote_port);
  }
  else {
    struct Curl_dns_entry *dns;
    Curl_addrinfo *hp=NULL;
    int rc = Curl_resolv(conn, hostname, remote_port, &dns);

    packetsize = 10;

    socksreq[3] = 1; /* IPv4 = 1 */

    if(rc == CURLRESOLV_ERROR)
      return CURLE_COULDNT_RESOLVE_HOST;

    if(rc == CURLRESOLV_PENDING)
      /* this requires that we're in "wait for resolve" state */
      rc = Curl_wait_for_resolv(conn, &dns);

    /*
     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
     * returns a Curl_addrinfo pointer that may not always look the same.
     */
    if(dns)
      hp=dns->addr;
    if(hp) {
      char buf[64];
      unsigned short ip[4];
      Curl_printable_address(hp, buf, sizeof(buf));

      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
                      &ip[0], &ip[1], &ip[2], &ip[3])) {
        socksreq[4] = (unsigned char)ip[0];
        socksreq[5] = (unsigned char)ip[1];
        socksreq[6] = (unsigned char)ip[2];
        socksreq[7] = (unsigned char)ip[3];
      }
      else
        hp = NULL; /* fail! */

      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
    }
    if(!hp) {
      failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
            hostname);
      return CURLE_COULDNT_RESOLVE_HOST;
    }

    *((unsigned short*)&socksreq[8]) = htons((unsigned short)remote_port);
  }

  code = Curl_write_plain(conn, sock, (char *)socksreq, packetsize, &written);
  if((code != CURLE_OK) || (written != packetsize)) {
    failf(data, "Failed to send SOCKS5 connect request.");
    return CURLE_COULDNT_CONNECT;
  }

  packetsize = 10; /* minimum packet size is 10 */

  result = blockread_all(conn, sock, (char *)socksreq, packetsize,
                           &actualread, timeout);
  if((result != CURLE_OK) || (actualread != packetsize)) {
    failf(data, "Failed to receive SOCKS5 connect request ack.");
    return CURLE_COULDNT_CONNECT;
  }

  if(socksreq[0] != 5) { /* version */
    failf(data,
          "SOCKS5 reply has wrong version, version should be 5.");
    return CURLE_COULDNT_CONNECT;
  }
  if(socksreq[1] != 0) { /* Anything besides 0 is an error */
      failf(data,
            "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
  }

  /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
     1928, so the reply packet should be read until the end to avoid errors at
     subsequent protocol level.

    +----+-----+-------+------+----------+----------+
    |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    +----+-----+-------+------+----------+----------+
    | 1  |  1  | X'00' |  1   | Variable |    2     |
    +----+-----+-------+------+----------+----------+

     ATYP:
     o  IP v4 address: X'01', BND.ADDR = 4 byte
     o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
     o  IP v6 address: X'04', BND.ADDR = 16 byte
     */

  /* Calculate real packet size */
  if(socksreq[3] == 3) {
    /* domain name */
    int addrlen = (int) socksreq[4];
    packetsize = 5 + addrlen + 2;
  }
  else if(socksreq[3] == 4) {
    /* IPv6 */
    packetsize = 4 + 16 + 2;
  }

  /* At this point we already read first 10 bytes */
  if(packetsize > 10) {
    packetsize -= 10;
    result = blockread_all(conn, sock, (char *)&socksreq[10], packetsize,
                           &actualread, timeout);
    if((result != CURLE_OK) || (actualread != packetsize)) {
      failf(data, "Failed to receive SOCKS5 connect request ack.");
      return CURLE_COULDNT_CONNECT;
    }
  }

  Curl_nonblock(sock, TRUE);
  return CURLE_OK; /* Proxy was successful! */
}
Пример #5
0
/*
* This function logs in to a SOCKS4 proxy and sends the specifics to the final
* destination server.
*
* Reference :
*   http://socks.permeo.com/protocol/socks4.protocol
*
* Note :
*   Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
*   Nonsupport "Identification Protocol (RFC1413)"
*/
CURLcode Curl_SOCKS4(const char *proxy_name,
                     struct connectdata *conn)
{
  unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */
  int result;
  CURLcode code;
  curl_socket_t sock = conn->sock[FIRSTSOCKET];
  long timeout;
  struct SessionHandle *data = conn->data;

  /* get timeout */
  if(data->set.timeout && data->set.connecttimeout) {
    if (data->set.timeout < data->set.connecttimeout)
      timeout = data->set.timeout*1000;
    else
      timeout = data->set.connecttimeout*1000;
  }
  else if(data->set.timeout)
    timeout = data->set.timeout*1000;
  else if(data->set.connecttimeout)
    timeout = data->set.connecttimeout*1000;
  else
    timeout = DEFAULT_CONNECT_TIMEOUT;

  Curl_nonblock(sock, FALSE);

  /*
   * Compose socks4 request
   *
   * Request format
   *
   *     +----+----+----+----+----+----+----+----+----+----+....+----+
   *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
   *     +----+----+----+----+----+----+----+----+----+----+....+----+
   * # of bytes:  1    1      2              4           variable       1
   */

  socksreq[0] = 4; /* version (SOCKS4) */
  socksreq[1] = 1; /* connect */
  *((unsigned short*)&socksreq[2]) = htons(conn->remote_port);

  /* DNS resolve */
  {
    struct Curl_dns_entry *dns;
    Curl_addrinfo *hp=NULL;
    int rc;

    rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);

    if(rc == CURLRESOLV_ERROR)
      return CURLE_COULDNT_RESOLVE_PROXY;

    if(rc == CURLRESOLV_PENDING)
      /* this requires that we're in "wait for resolve" state */
      rc = Curl_wait_for_resolv(conn, &dns);

    /*
     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
     * returns a Curl_addrinfo pointer that may not always look the same.
     */
    if(dns)
      hp=dns->addr;
    if (hp) {
      char buf[64];
      unsigned short ip[4];
      Curl_printable_address(hp, buf, sizeof(buf));

      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
                      &ip[0], &ip[1], &ip[2], &ip[3])) {
        /* Set DSTIP */
        socksreq[4] = (unsigned char)ip[0];
        socksreq[5] = (unsigned char)ip[1];
        socksreq[6] = (unsigned char)ip[2];
        socksreq[7] = (unsigned char)ip[3];
      }
      else
        hp = NULL; /* fail! */

      Curl_resolv_unlock(data, dns); /* not used anymore from now on */

    }
    if(!hp) {
      failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
            conn->host.name);
      return CURLE_COULDNT_RESOLVE_HOST;
    }
  }

  /*
   * This is currently not supporting "Identification Protocol (RFC1413)".
   */
  socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
  if (proxy_name)
    strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);

  /*
   * Make connection
   */
  {
    ssize_t actualread;
    ssize_t written;
    int packetsize = 9 +
      (int)strlen((char*)socksreq + 8); /* size including NUL */

    /* Send request */
    code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
    if ((code != CURLE_OK) || (written != packetsize)) {
      failf(data, "Failed to send SOCKS4 connect request.");
      return CURLE_COULDNT_CONNECT;
    }

    packetsize = 8; /* receive data size */

    /* Receive response */
    result = blockread_all(conn, sock, (char *)socksreq, packetsize,
                           &actualread, timeout);
    if ((result != CURLE_OK) || (actualread != packetsize)) {
      failf(data, "Failed to receive SOCKS4 connect request ack.");
      return CURLE_COULDNT_CONNECT;
    }

    /*
     * Response format
     *
     *     +----+----+----+----+----+----+----+----+
     *     | VN | CD | DSTPORT |      DSTIP        |
     *     +----+----+----+----+----+----+----+----+
     * # of bytes:  1    1      2              4
     *
     * VN is the version of the reply code and should be 0. CD is the result
     * code with one of the following values:
     *
     * 90: request granted
     * 91: request rejected or failed
     * 92: request rejected because SOCKS server cannot connect to
     *     identd on the client
     * 93: request rejected because the client program and identd
     *     report different user-ids
     */

    /* wrong version ? */
    if (socksreq[0] != 0) {
      failf(data,
            "SOCKS4 reply has wrong version, version should be 4.");
      return CURLE_COULDNT_CONNECT;
    }

    /* Result */
    switch(socksreq[1])
    {
    case 90:
      infof(data, "SOCKS4 request granted.\n");
      break;
    case 91:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", request rejected or failed.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    case 92:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", request rejected because SOCKS server cannot connect to "
            "identd on the client.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    case 93:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", request rejected because the client program and identd "
            "report different user-ids.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    default:
      failf(data,
            "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
            ", Unknown.",
            (unsigned char)socksreq[4], (unsigned char)socksreq[5],
            (unsigned char)socksreq[6], (unsigned char)socksreq[7],
            (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
            socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    }
  }

  Curl_nonblock(sock, TRUE);

  return CURLE_OK; /* Proxy was successful! */
}
Пример #6
0
/*
 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
 * destination server.
 */
CURLcode Curl_SOCKS5(const char *proxy_name,
                     const char *proxy_password,
                     struct connectdata *conn)
{
  /*
    According to the RFC1928, section "6.  Replies". This is what a SOCK5
    replies:

        +----+-----+-------+------+----------+----------+
        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
        +----+-----+-------+------+----------+----------+
        | 1  |  1  | X'00' |  1   | Variable |    2     |
        +----+-----+-------+------+----------+----------+

    Where:

    o  VER    protocol version: X'05'
    o  REP    Reply field:
    o  X'00' succeeded
  */

  unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
  ssize_t actualread;
  ssize_t written;
  int result;
  CURLcode code;
  curl_socket_t sock = conn->sock[FIRSTSOCKET];
  struct SessionHandle *data = conn->data;
  long timeout;

  /* get timeout */
  if(data->set.timeout && data->set.connecttimeout) {
    if (data->set.timeout < data->set.connecttimeout)
      timeout = data->set.timeout*1000;
    else
      timeout = data->set.connecttimeout*1000;
  }
  else if(data->set.timeout)
    timeout = data->set.timeout*1000;
  else if(data->set.connecttimeout)
    timeout = data->set.connecttimeout*1000;
  else
    timeout = DEFAULT_CONNECT_TIMEOUT;

  Curl_nonblock(sock, TRUE);

  /* wait until socket gets connected */
  result = Curl_select(CURL_SOCKET_BAD, sock, (int)timeout);

  if(-1 == result) {
    failf(conn->data, "SOCKS5: no connection here");
    return CURLE_COULDNT_CONNECT;
  }
  else if(0 == result) {
    failf(conn->data, "SOCKS5: connection timeout");
    return CURLE_OPERATION_TIMEDOUT;
  }

  if(result & CSELECT_ERR) {
    failf(conn->data, "SOCKS5: error occured during connection");
    return CURLE_COULDNT_CONNECT;
  }

  socksreq[0] = 5; /* version */
  socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
  socksreq[2] = 0; /* no authentication */
  socksreq[3] = 2; /* username/password */

  Curl_nonblock(sock, FALSE);

  code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
                      &written);
  if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
    failf(data, "Unable to send initial SOCKS5 request.");
    return CURLE_COULDNT_CONNECT;
  }

  Curl_nonblock(sock, TRUE);

  result = Curl_select(sock, CURL_SOCKET_BAD, (int)timeout);

  if(-1 == result) {
    failf(conn->data, "SOCKS5 nothing to read");
    return CURLE_COULDNT_CONNECT;
  }
  else if(0 == result) {
    failf(conn->data, "SOCKS5 read timeout");
    return CURLE_OPERATION_TIMEDOUT;
  }

  if(result & CSELECT_ERR) {
    failf(conn->data, "SOCKS5 read error occured");
    return CURLE_RECV_ERROR;
  }

  Curl_nonblock(sock, FALSE);

  result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread, timeout);
  if ((result != CURLE_OK) || (actualread != 2)) {
    failf(data, "Unable to receive initial SOCKS5 response.");
    return CURLE_COULDNT_CONNECT;
  }

  if (socksreq[0] != 5) {
    failf(data, "Received invalid version in initial SOCKS5 response.");
    return CURLE_COULDNT_CONNECT;
  }
  if (socksreq[1] == 0) {
    /* Nothing to do, no authentication needed */
    ;
  }
  else if (socksreq[1] == 2) {
    /* Needs user name and password */
    size_t userlen, pwlen;
    int len;
    if(proxy_name && proxy_password) {
      userlen = strlen(proxy_name);
      pwlen = proxy_password?strlen(proxy_password):0;
    }
    else {
      userlen = 0;
      pwlen = 0;
    }

    /*   username/password request looks like
     * +----+------+----------+------+----------+
     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
     * +----+------+----------+------+----------+
     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
     * +----+------+----------+------+----------+
     */
    len = 0;
    socksreq[len++] = 1;    /* username/pw subnegotiation version */
    socksreq[len++] = (char) userlen;
    memcpy(socksreq + len, proxy_name, (int) userlen);
    len += userlen;
    socksreq[len++] = (char) pwlen;
    memcpy(socksreq + len, proxy_password, (int) pwlen);
    len += pwlen;

    code = Curl_write(conn, sock, (char *)socksreq, len, &written);
    if ((code != CURLE_OK) || (len != written)) {
      failf(data, "Failed to send SOCKS5 sub-negotiation request.");
      return CURLE_COULDNT_CONNECT;
    }

    result=blockread_all(conn, sock, (char *)socksreq, 2, &actualread,
                         timeout);
    if ((result != CURLE_OK) || (actualread != 2)) {
      failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
      return CURLE_COULDNT_CONNECT;
    }

    /* ignore the first (VER) byte */
    if (socksreq[1] != 0) { /* status */
      failf(data, "User was rejected by the SOCKS5 server (%d %d).",
            socksreq[0], socksreq[1]);
      return CURLE_COULDNT_CONNECT;
    }

    /* Everything is good so far, user was authenticated! */
  }
  else {
    /* error */
    if (socksreq[1] == 1) {
      failf(data,
            "SOCKS5 GSSAPI per-message authentication is not supported.");
      return CURLE_COULDNT_CONNECT;
    }
    else if (socksreq[1] == 255) {
      if (!proxy_name || !*proxy_name) {
        failf(data,
              "No authentication method was acceptable. (It is quite likely"
              " that the SOCKS5 server wanted a username/password, since none"
              " was supplied to the server on this connection.)");
      }
      else {
        failf(data, "No authentication method was acceptable.");
      }
      return CURLE_COULDNT_CONNECT;
    }
    else {
      failf(data,
            "Undocumented SOCKS5 mode attempted to be used by server.");
      return CURLE_COULDNT_CONNECT;
    }
  }

  /* Authentication is complete, now specify destination to the proxy */
  socksreq[0] = 5; /* version (SOCKS5) */
  socksreq[1] = 1; /* connect */
  socksreq[2] = 0; /* must be zero */
  socksreq[3] = 1; /* IPv4 = 1 */

  {
    struct Curl_dns_entry *dns;
    Curl_addrinfo *hp=NULL;
    int rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);

    if(rc == CURLRESOLV_ERROR)
      return CURLE_COULDNT_RESOLVE_HOST;

    if(rc == CURLRESOLV_PENDING)
      /* this requires that we're in "wait for resolve" state */
      rc = Curl_wait_for_resolv(conn, &dns);

    /*
     * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
     * returns a Curl_addrinfo pointer that may not always look the same.
     */
    if(dns)
      hp=dns->addr;
    if (hp) {
      char buf[64];
      unsigned short ip[4];
      Curl_printable_address(hp, buf, sizeof(buf));

      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
                      &ip[0], &ip[1], &ip[2], &ip[3])) {
        socksreq[4] = (unsigned char)ip[0];
        socksreq[5] = (unsigned char)ip[1];
        socksreq[6] = (unsigned char)ip[2];
        socksreq[7] = (unsigned char)ip[3];
      }
      else
        hp = NULL; /* fail! */

      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
    }
    if(!hp) {
      failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
            conn->host.name);
      return CURLE_COULDNT_RESOLVE_HOST;
    }
  }

  *((unsigned short*)&socksreq[8]) = htons(conn->remote_port);

  {
    const int packetsize = 10;

    code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
    if ((code != CURLE_OK) || (written != packetsize)) {
      failf(data, "Failed to send SOCKS5 connect request.");
      return CURLE_COULDNT_CONNECT;
    }

    result = blockread_all(conn, sock, (char *)socksreq, packetsize,
                           &actualread, timeout);
    if ((result != CURLE_OK) || (actualread != packetsize)) {
      failf(data, "Failed to receive SOCKS5 connect request ack.");
      return CURLE_COULDNT_CONNECT;
    }

    if (socksreq[0] != 5) { /* version */
      failf(data,
            "SOCKS5 reply has wrong version, version should be 5.");
      return CURLE_COULDNT_CONNECT;
    }
    if (socksreq[1] != 0) { /* Anything besides 0 is an error */
        failf(data,
              "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
              (unsigned char)socksreq[4], (unsigned char)socksreq[5],
              (unsigned char)socksreq[6], (unsigned char)socksreq[7],
              (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
              socksreq[1]);
        return CURLE_COULDNT_CONNECT;
    }
  }

  Curl_nonblock(sock, TRUE);
  return CURLE_OK; /* Proxy was successful! */
}
Пример #7
0
static CURLcode bindlocal(struct connectdata *conn,
                          curl_socket_t sockfd)
{
#ifdef HAVE_INET_NTOA
  bool bindworked = FALSE;
  struct SessionHandle *data = conn->data;

  /*************************************************************
   * Select device to bind socket to
   *************************************************************/
  if (strlen(data->set.device)<255) {
    struct Curl_dns_entry *h=NULL;
    size_t size;
    char myhost[256] = "";
    in_addr_t in;
    int rc;
    bool was_iface = FALSE;

    /* First check if the given name is an IP address */
    in=inet_addr(data->set.device);

    if((in == CURL_INADDR_NONE) &&
       Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
      /*
       * We now have the numerical IPv4-style x.y.z.w 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) {
      /*
       * This was not an interface, resolve the name as a host name
       * or IP number
       */
      rc = Curl_resolv(conn, data->set.device, 0, &h);
      if(rc == CURLRESOLV_PENDING)
        (void)Curl_wait_for_resolv(conn, &h);

      if(h)
        /* we know data->set.device is shorter than the myhost array */
        strcpy(myhost, data->set.device);
    }

    if(! *myhost) {
      /* need to fix this
         h=Curl_gethost(data,
         getmyhost(*myhost,sizeof(myhost)),
         hostent_buf,
         sizeof(hostent_buf));
      */
      failf(data, "Couldn't bind to '%s'", data->set.device);
      return CURLE_HTTP_PORT_FAILED;
    }

    infof(data, "We bind local end to %s\n", myhost);

#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,
                     data->set.device, strlen(data->set.device)+1) != 0) {
        /* printf("Failed to BINDTODEVICE, socket: %d  device: %s error: %s\n",
           sockfd, data->set.device, Curl_strerror(Curl_ourerrno())); */
        infof(data, "SO_BINDTODEVICE %s failed\n",
              data->set.device);
        /* 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);
    if (CURL_INADDR_NONE != in) {

      if ( h ) {
        Curl_addrinfo *addr = h->addr;

        Curl_resolv_unlock(data, h);
        /* we don't need it anymore after this function has returned */

        if( bind(sockfd, addr->ai_addr, (socklen_t)addr->ai_addrlen) >= 0) {
          /* we succeeded to bind */
#ifdef ENABLE_IPV6
          struct sockaddr_in6 add;
#else
          struct sockaddr_in add;
#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,"could't find my own IP address (%s)", myhost);
        return CURLE_HTTP_PORT_FAILED;
      }
    } /* end of inet_addr */

    else {
      failf(data, "could't find my own IP address (%s)", myhost);
      return CURLE_HTTP_PORT_FAILED;
    }

    return CURLE_OK;

  } /* end of device selection support */
#else
  (void)conn;
  (void)sockfd;
#endif /* end of HAVE_INET_NTOA */

  return CURLE_HTTP_PORT_FAILED;
}
Пример #8
0
int Curl_resolv_timeout(struct connectdata *conn,
                        const char *hostname,
                        int port,
                        struct Curl_dns_entry **entry,
                        long timeoutms)
{
#ifdef USE_ALARM_TIMEOUT
#ifdef HAVE_SIGACTION
  struct sigaction keep_sigact;   /* store the old struct here */
  volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
  struct sigaction sigact;
#else
#ifdef HAVE_SIGNAL
  void (*keep_sigact)(int);       /* store the old handler here */
#endif /* HAVE_SIGNAL */
#endif /* HAVE_SIGACTION */
  volatile long timeout;
  volatile unsigned int prev_alarm = 0;
  struct SessionHandle *data = conn->data;
#endif /* USE_ALARM_TIMEOUT */
  int rc;

  *entry = NULL;

#ifdef USE_ALARM_TIMEOUT
  if (data->set.no_signal)
    /* Ignore the timeout when signals are disabled */
    timeout = 0;
  else
    timeout = timeoutms;

  if(!timeout)
    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
    return Curl_resolv(conn, hostname, port, entry);

  if(timeout < 1000)
    /* The alarm() function only provides integer second resolution, so if
       we want to wait less than one second we must bail out already now. */
    return CURLRESOLV_TIMEDOUT;

  /*************************************************************
   * Set signal handler to catch SIGALRM
   * Store the old value to be able to set it back later!
   *************************************************************/
#ifdef HAVE_SIGACTION
  sigaction(SIGALRM, NULL, &sigact);
  keep_sigact = sigact;
  keep_copysig = TRUE; /* yes, we have a copy */
  sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
  /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
  sigact.sa_flags &= ~SA_RESTART;
#endif
  /* now set the new struct */
  sigaction(SIGALRM, &sigact, NULL);
#else /* HAVE_SIGACTION */
  /* no sigaction(), revert to the much lamer signal() */
#ifdef HAVE_SIGNAL
  keep_sigact = signal(SIGALRM, alarmfunc);
#endif
#endif /* HAVE_SIGACTION */

  /* alarm() makes a signal get sent when the timeout fires off, and that
     will abort system calls */
  prev_alarm = alarm(curlx_sltoui(timeout/1000L));

  /* This allows us to time-out from the name resolver, as the timeout
     will generate a signal and we will siglongjmp() from that here.
     This technique has problems (see alarmfunc).
     This should be the last thing we do before calling Curl_resolv(),
     as otherwise we'd have to worry about variables that get modified
     before we invoke Curl_resolv() (and thus use "volatile"). */
  if(sigsetjmp(curl_jmpenv, 1)) {
    /* this is coming from a siglongjmp() after an alarm signal */
    failf(data, "name lookup timed out");
    rc = CURLRESOLV_ERROR;
    goto clean_up;
  }

#else
#ifndef CURLRES_ASYNCH
  if(timeoutms)
    infof(conn->data, "timeout on name lookup is not supported\n");
#else
  (void)timeoutms; /* timeoutms not used with an async resolver */
#endif
#endif /* USE_ALARM_TIMEOUT */

  /* Perform the actual name resolution. This might be interrupted by an
   * alarm if it takes too long.
   */
  rc = Curl_resolv(conn, hostname, port, entry);

#ifdef USE_ALARM_TIMEOUT
clean_up:

  if(!prev_alarm)
    /* deactivate a possibly active alarm before uninstalling the handler */
    alarm(0);

#ifdef HAVE_SIGACTION
  if(keep_copysig) {
    /* we got a struct as it looked before, now put that one back nice
       and clean */
    sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
  }
#else
#ifdef HAVE_SIGNAL
  /* restore the previous SIGALRM handler */
  signal(SIGALRM, keep_sigact);
#endif
#endif /* HAVE_SIGACTION */

  /* switch back the alarm() to either zero or to what it was before minus
     the time we spent until now! */
  if(prev_alarm) {
    /* there was an alarm() set before us, now put it back */
    unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);

    /* the alarm period is counted in even number of seconds */
    unsigned long alarm_set = prev_alarm - elapsed_ms/1000;

    if(!alarm_set ||
       ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
      /* if the alarm time-left reached zero or turned "negative" (counted
         with unsigned values), we should fire off a SIGALRM here, but we
         won't, and zero would be to switch it off so we never set it to
         less than 1! */
      alarm(1);
      rc = CURLRESOLV_TIMEDOUT;
      failf(data, "Previous alarm fired off!");
    }
    else
      alarm((unsigned int)alarm_set);
  }
#endif /* USE_ALARM_TIMEOUT */

  return rc;
}
Пример #9
0
static CURLcode bindlocal(struct connectdata *conn,
                          curl_socket_t sockfd, int af)
{
  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 = 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 */
  int portnum = data->set.localportrange;
  const char *dev = data->set.str[STRING_DEVICE];
  int error;

  /*************************************************************
   * Select device to bind socket to
   *************************************************************/
  if(dev && (strlen(dev)<255) ) {
    char myhost[256] = "";
    int rc;
    bool was_iface = FALSE;

    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) {
      /*
       * 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;
#ifdef ENABLE_IPV6
      else if (af == AF_INET6)
        data->set.ip_version = CURL_IPRESOLVE_V6;
#endif

      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) {
        /* 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_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 */
      }
    }
#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);

    }
#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);
    }
#endif
  }
  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 */
      struct Curl_sockaddr_storage add;
      socklen_t size = sizeof(add);
      memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
      if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
        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 */
    }
    else
      break;
  } while(1);

  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;
}
Пример #10
0
static CURLcode bindlocal(struct connectdata *conn,
                          curl_socket_t sockfd, int af)
{
  struct SessionHandle *data = conn->data;

  struct Curl_sockaddr_storage sa;
  struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
  curl_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 *si6 = (struct sockaddr_in6 *)&sa;
#endif

  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 */
  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 */
  bool is_interface = FALSE;
  bool is_host = FALSE;
  static const char *if_prefix = "if!";
  static const char *host_prefix = "host!";

  /*************************************************************
   * 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) ) {
    if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
      dev += strlen(if_prefix);
      is_interface = TRUE;
    }
    else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
      dev += strlen(host_prefix);
      is_host = TRUE;
    }

    /* interface */
    if(!is_host) {
		return CURLE_UNSUPPORTED_PROTOCOL;
    }
    if(!is_interface) {
      /*
       * 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 = conn->ip_version;
      int rc;

      if(af == AF_INET)
        conn->ip_version = CURL_IPRESOLVE_V4;
#ifdef ENABLE_IPV6
      else if(af == AF_INET6)
        conn->ip_version = CURL_IPRESOLVE_V6;
#endif

      rc = Curl_resolv(conn, dev, 0, &h);
      if(rc == CURLRESOLV_PENDING)
        (void)Curl_resolver_wait_resolv(conn, &h);
      conn->ip_version = ipver;

      if(h) {
        /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
        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);
        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;
      }
    }

    if(done > 0) {
#ifdef ENABLE_IPV6
      /* ipv6 address */
      if(af == AF_INET6) {
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
        char *scope_ptr = strchr(myhost, '%');
        if(scope_ptr)
          *(scope_ptr++) = 0;
#endif
        if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
          si6->sin6_family = AF_INET6;
          si6->sin6_port = htons(port);
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
          if(scope_ptr)
            /* The "myhost" string either comes from Curl_if2ip or from
               Curl_printable_address. The latter returns only numeric scope
               IDs and the former returns none at all.  So the scope ID, if
               present, is known to be numeric */
            si6->sin6_scope_id = atoi(scope_ptr);
#endif
        }
        sizeof_sa = sizeof(struct sockaddr_in6);
      }
      else
#endif
      /* ipv4 address */
      if((af == AF_INET) &&
         (Curl_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
    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);
    }
  }

  for(;;) {
    if(bind(sockfd, sock, sizeof_sa) >= 0) {
      /* we succeeded to bind */
      struct Curl_sockaddr_storage add;
      curl_socklen_t size = sizeof(add);
      memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
      if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
        data->state.os_errno = error = SOCKERRNO;
        failf(data, "getsockname() failed with errno %d: %s",
              error, Curl_strerror(conn, error));
        return CURLE_INTERFACE_FAILED;
      }
      infof(data, "Local port: %hu\n", port);
      conn->bits.bound = TRUE;
      return CURLE_OK;
    }

    if(--portnum > 0) {
      infof(data, "Bind to local port %hu 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;
  }

  data->state.os_errno = error = SOCKERRNO;
  failf(data, "bind failed with errno %d: %s",
        error, Curl_strerror(conn, error));

  return CURLE_INTERFACE_FAILED;
}
Пример #11
0
static CURLcode bindlocal(struct connectdata *conn,
                          curl_socket_t sockfd)
{
#ifdef HAVE_INET_NTOA
  bool bindworked = FALSE;
  struct SessionHandle *data = conn->data;

  /*************************************************************
   * Select device to bind socket to
   *************************************************************/
  if (strlen(data->set.device)<255) {
    struct Curl_dns_entry *h=NULL;
    size_t size;
    char myhost[256] = "";
    in_addr_t in;
    int rc;
    bool was_iface = FALSE;

    /* First check if the given name is an IP address */
    in=inet_addr(data->set.device);
      
    if((in == CURL_INADDR_NONE) &&
       Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
      /*
       * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
       */
      rc = Curl_resolv(conn, myhost, 0, &h);
      if(rc == 1)
        (void)Curl_wait_for_resolv(conn, &h);

      if(h)
        was_iface = TRUE;
    }

    if(!was_iface) {
      /*
       * This was not an interface, resolve the name as a host name
       * or IP number
       */
      rc = Curl_resolv(conn, data->set.device, 0, &h);
      if(rc == 1)
        (void)Curl_wait_for_resolv(conn, &h);

      if(h)
        /* we know data->set.device is shorter than the myhost array */
        strcpy(myhost, data->set.device);
    }

    if(! *myhost) {
      /* need to fix this
         h=Curl_gethost(data,
         getmyhost(*myhost,sizeof(myhost)),
         hostent_buf,
         sizeof(hostent_buf));
      */
      failf(data, "Couldn't bind to '%s'", data->set.device);
      return CURLE_HTTP_PORT_FAILED;
    }

    infof(data, "We bind local end to %s\n", myhost);

#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,
                     data->set.device, strlen(data->set.device)+1) != 0) {
        /* printf("Failed to BINDTODEVICE, socket: %d  device: %s error: %s\n",
           sockfd, data->set.device, strerror(errno)); */
        infof(data, "SO_BINDTODEVICE %s failed\n",
              data->set.device);
        /* This is typiclally "errno 1, error: Operation not permitted" if
           you're not running as root or another suitable privileged user */
      }
    }
#endif

    in=inet_addr(myhost);
    if (CURL_INADDR_NONE != in) {

      if ( h ) {
        Curl_addrinfo *addr = h->addr;

        Curl_resolv_unlock(data, h);
        /* we don't need it anymore after this function has returned */

#ifdef ENABLE_IPV6
        if( bind(sockfd, addr->ai_addr, addr->ai_addrlen) >= 0) {
          /* we succeeded to bind */
          struct sockaddr_in6 add;

          bindworked = TRUE;
	
          size = sizeof(add);
          if(getsockname(sockfd, (struct sockaddr *) &add,
                         (socklen_t *)&size)<0) {
            failf(data, "getsockname() failed");
            return CURLE_HTTP_PORT_FAILED;
          }
        }
#else
        {
          struct sockaddr_in sa;

          memset((char *)&sa, 0, sizeof(sa));
          memcpy((char *)&sa.sin_addr, addr->h_addr, addr->h_length);
          sa.sin_family = AF_INET;
          sa.sin_addr.s_addr = in;
          sa.sin_port = 0; /* get any port */
	
          if( bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) >= 0) {
            /* we succeeded to bind */
            struct sockaddr_in add;
	
            bindworked = TRUE;
            
            size = sizeof(add);
            if(getsockname(sockfd, (struct sockaddr *) &add,
                           (socklen_t *)&size)<0) {
              failf(data, "getsockname() failed");
              return CURLE_HTTP_PORT_FAILED;
            }
          }
        }
#endif
        if(!bindworked) {
          switch(errno) {
          case EBADF:
            failf(data, "Invalid descriptor: %d", errno);
            break;
          case EINVAL:
            failf(data, "Invalid request: %d", errno);
            break;
          case EACCES:
            failf(data, "Address is protected, user not superuser: %d", errno);
            break;
          case ENOTSOCK:
            failf(data,
                  "Argument is a descriptor for a file, not a socket: %d",
                  errno);
            break;
          case EFAULT:
            failf(data, "Inaccessable memory error: %d", errno);
            break;
          case ENAMETOOLONG:
            failf(data, "Address too long: %d", errno);
            break;
          case ENOMEM:
            failf(data, "Insufficient kernel memory was available: %d", errno);
            break;
          default:
            failf(data, "errno %d", errno);
            break;
          } /* end of switch(errno) */
	
          return CURLE_HTTP_PORT_FAILED;
        } /* end of else */
	
      } /* end of if  h */
      else {
	failf(data,"could't find my own IP address (%s)", myhost);
	return CURLE_HTTP_PORT_FAILED;
      }
    } /* end of inet_addr */

    else {
      failf(data, "could't find my own IP address (%s)", myhost);
      return CURLE_HTTP_PORT_FAILED;
    }

    return CURLE_OK;

  } /* end of device selection support */
#endif /* end of HAVE_INET_NTOA */

  return CURLE_HTTP_PORT_FAILED;
}
Пример #12
0
static CURLcode bindlocal(struct connectdata *conn,
                          int sockfd)
{
#if !defined(WIN32)||defined(__CYGWIN32__)
  /* We don't generally like checking for OS-versions, we should make this
     HAVE_XXXX based, although at the moment I don't have a decent test for
     this! */

#ifdef HAVE_INET_NTOA

  struct SessionHandle *data = conn->data;

  /*************************************************************
   * Select device to bind socket to
   *************************************************************/
  if (strlen(data->set.device)<255) {
    struct sockaddr_in sa;
    struct Curl_dns_entry *h=NULL;
    size_t size;
    char myhost[256] = "";
    in_addr_t in;

    /* First check if the given name is an IP address */
    in=inet_addr(data->set.device);
      
    if((in == CURL_INADDR_NONE) &&
       Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
      /*
       * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
       */
      h = Curl_resolv(data, myhost, 0);
    }
    else {
      if(strlen(data->set.device)>1) {
        /*
         * This was not an interface, resolve the name as a host name
         * or IP number
         */
        h = Curl_resolv(data, data->set.device, 0);
        if(h) {
          /* we know data->set.device is shorter than the myhost array */
          strcpy(myhost, data->set.device);
        }
      }
    }

    if(! *myhost) {
      /* need to fix this
         h=Curl_gethost(data,
         getmyhost(*myhost,sizeof(myhost)),
         hostent_buf,
         sizeof(hostent_buf));
      */
      return CURLE_HTTP_PORT_FAILED;
    }

    infof(data, "We bind local end to %s\n", myhost);

    in=inet_addr(myhost);
    if (CURL_INADDR_NONE != in) {

      if ( h ) {
        Curl_addrinfo *addr = h->addr;

        Curl_resolv_unlock(data, h);
        /* we don't need it anymore after this function has returned */

#ifdef ENABLE_IPV6
        (void)sa; /* prevent compiler warning */
        if( bind(sockfd, addr->ai_addr, addr->ai_addrlen) >= 0) {
          /* we succeeded to bind */
          struct sockaddr_in6 add;
	
          size = sizeof(add);
          if(getsockname(sockfd, (struct sockaddr *) &add,
                         (socklen_t *)&size)<0) {
            failf(data, "getsockname() failed");
            return CURLE_HTTP_PORT_FAILED;
          }
        }
#else
        memset((char *)&sa, 0, sizeof(sa));
        memcpy((char *)&sa.sin_addr, addr->h_addr, addr->h_length);
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = in;
        sa.sin_port = 0; /* get any port */
	
        if( bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) >= 0) {
          /* we succeeded to bind */
          struct sockaddr_in add;
	
          size = sizeof(add);
          if(getsockname(sockfd, (struct sockaddr *) &add,
                         (socklen_t *)&size)<0) {
            failf(data, "getsockname() failed");
            return CURLE_HTTP_PORT_FAILED;
          }
        }
#endif
        else {
          switch(errno) {
          case EBADF:
            failf(data, "Invalid descriptor: %d", errno);
            break;
          case EINVAL:
            failf(data, "Invalid request: %d", errno);
            break;
          case EACCES:
            failf(data, "Address is protected, user not superuser: %d", errno);
            break;
          case ENOTSOCK:
            failf(data,
                  "Argument is a descriptor for a file, not a socket: %d",
                  errno);
            break;
          case EFAULT:
            failf(data, "Inaccessable memory error: %d", errno);
            break;
          case ENAMETOOLONG:
            failf(data, "Address too long: %d", errno);
            break;
          case ENOMEM:
            failf(data, "Insufficient kernel memory was available: %d", errno);
            break;
          default:
            failf(data, "errno %d", errno);
            break;
          } /* end of switch(errno) */
	
          return CURLE_HTTP_PORT_FAILED;
        } /* end of else */
	
      } /* end of if  h */
      else {
	failf(data,"could't find my own IP address (%s)", myhost);
	return CURLE_HTTP_PORT_FAILED;
      }
    } /* end of inet_addr */

    else {
      failf(data, "could't find my own IP address (%s)", myhost);
      return CURLE_HTTP_PORT_FAILED;
    }

    return CURLE_OK;

  } /* end of device selection support */
#endif /* end of HAVE_INET_NTOA */
#endif /* end of not WIN32 */

  return CURLE_HTTP_PORT_FAILED;
}
Пример #13
0
static CURLcode bindlocal(struct connectdata *conn,
                          curl_socket_t sockfd, int af, unsigned int scope)
{
  struct Curl_easy *data = conn->data;

  struct Curl_sockaddr_storage sa;
  struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
  curl_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 *si6 = (struct sockaddr_in6 *)&sa;
#endif

  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 */
  int portnum = data->set.localportrange;
  const char *dev = data->set.str[STRING_DEVICE];
  int error;

  /*************************************************************
   * 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 done = 0; /* -1 for error, 1 for address found */
    bool is_interface = FALSE;
    bool is_host = FALSE;
    static const char *if_prefix = "if!";
    static const char *host_prefix = "host!";

    if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
      dev += strlen(if_prefix);
      is_interface = TRUE;
    }
    else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
      dev += strlen(host_prefix);
      is_host = TRUE;
    }

    /* interface */
    if(!is_host) {
#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.
       *
       * interface might be a VRF, eg: vrf-blue, which means it cannot be
       * converted to an IP address and would fail Curl_if2ip. Simply try
       * to use it straight away.
       */
      if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
                    dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
        /* This is typically "errno 1, error: Operation not permitted" if
         * you're not running as root or another suitable privileged
         * user.
         * If it succeeds it means the parameter was a valid interface and
         * not an IP address. Return immediately.
         */
        return CURLE_OK;
      }
#endif

      switch(Curl_if2ip(af, scope, conn->scope_id, dev,
                        myhost, sizeof(myhost))) {
        case IF2IP_NOT_FOUND:
          if(is_interface) {
            /* Do not fall back to treating it as a host name */
            failf(data, "Couldn't bind to interface '%s'", dev);
            return CURLE_INTERFACE_FAILED;
          }
          break;
        case IF2IP_AF_NOT_SUPPORTED:
          /* Signal the caller to try another address family if available */
          return CURLE_UNSUPPORTED_PROTOCOL;
        case IF2IP_FOUND:
          is_interface = TRUE;
          /*
           * We now have the numerical IP address in the 'myhost' buffer
           */
          infof(data, "Local Interface %s is ip %s using address family %i\n",
                dev, myhost, af);
          done = 1;
          break;
      }
    }
    if(!is_interface) {
      /*
       * 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 = conn->ip_version;
      int rc;

      if(af == AF_INET)
        conn->ip_version = CURL_IPRESOLVE_V4;
#ifdef ENABLE_IPV6
      else if(af == AF_INET6)
        conn->ip_version = CURL_IPRESOLVE_V6;
#endif

      rc = Curl_resolv(conn, dev, 0, &h);
      if(rc == CURLRESOLV_PENDING)
        (void)Curl_resolver_wait_resolv(conn, &h);
      conn->ip_version = ipver;

      if(h) {
        /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
        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);
        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;
      }
    }

    if(done > 0) {
#ifdef ENABLE_IPV6
      /* IPv6 address */
      if(af == AF_INET6) {
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
        char *scope_ptr = strchr(myhost, '%');
        if(scope_ptr)
          *(scope_ptr++) = 0;
#endif
        if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
          si6->sin6_family = AF_INET6;
          si6->sin6_port = htons(port);
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
          if(scope_ptr)
            /* The "myhost" string either comes from Curl_if2ip or from
               Curl_printable_address. The latter returns only numeric scope
               IDs and the former returns none at all.  So the scope ID, if
               present, is known to be numeric */
            si6->sin6_scope_id = atoi(scope_ptr);
#endif
        }
        sizeof_sa = sizeof(struct sockaddr_in6);
      }
      else
#endif
      /* IPv4 address */
      if((af == AF_INET) &&
         (Curl_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) {
      /* errorbuf is set false so failf will overwrite any message already in
         the error buffer, so the user receives this error message instead of a
         generic resolve error. */
      data->state.errorbuf = FALSE;
      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
    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);
    }
  }

  for(;;) {
    if(bind(sockfd, sock, sizeof_sa) >= 0) {
      /* we succeeded to bind */
      struct Curl_sockaddr_storage add;
      curl_socklen_t size = sizeof(add);
      memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
      if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
        data->state.os_errno = error = SOCKERRNO;
        failf(data, "getsockname() failed with errno %d: %s",
              error, Curl_strerror(conn, error));
        return CURLE_INTERFACE_FAILED;
      }
      infof(data, "Local port: %hu\n", port);
      conn->bits.bound = TRUE;
      return CURLE_OK;
    }

    if(--portnum > 0) {
      infof(data, "Bind to local port %hu 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;
  }

  data->state.os_errno = error = SOCKERRNO;
  failf(data, "bind failed with errno %d: %s",
        error, Curl_strerror(conn, error));

  return CURLE_INTERFACE_FAILED;
}