Commit d44b0142 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

FTP: make the data connection work when going through proxy

This is a regression since the switch to always-multi internally
c4312741.

Test 1316 was modified since we now clearly call the Curl_client_write()
function when doing the LIST transfer part and then the
handler->protocol says FTP and ftpc.transfertype is 'A' which implies
text converting even though that the response is initially a HTTP
CONNECT response in this case.
parent 469b4233
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -777,7 +777,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
      /* we are connected with TCP, awesome! */
      /* we are connected with TCP, awesome! */


      /* see if we need to do any proxy magic first once we connected */
      /* see if we need to do any proxy magic first once we connected */
      code = Curl_connected_proxy(conn);
      code = Curl_connected_proxy(conn, sockindex);
      if(code)
      if(code)
        return code;
        return code;


+98 −86
Original line number Original line Diff line number Diff line
@@ -1802,6 +1802,79 @@ static CURLcode ftp_epsv_disable(struct connectdata *conn)
  return result;
  return result;
}
}


/*
 * Perform the necessary magic that needs to be done once the TCP connection
 * to the proxy has completed.
 */
static CURLcode proxy_magic(struct connectdata *conn,
                            char *newhost, unsigned short newport,
                            bool *magicdone)
{
  struct SessionHandle *data=conn->data;
  CURLcode result;

  *magicdone = FALSE;
  switch(conn->proxytype) {
  case CURLPROXY_SOCKS5:
  case CURLPROXY_SOCKS5_HOSTNAME:
    result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost,
                         newport, SECONDARYSOCKET, conn);
    *magicdone = TRUE;
    break;
  case CURLPROXY_SOCKS4:
    result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
                         SECONDARYSOCKET, conn, FALSE);
    *magicdone = TRUE;
    break;
  case CURLPROXY_SOCKS4A:
    result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
                         SECONDARYSOCKET, conn, TRUE);
    *magicdone = TRUE;
    break;
  case CURLPROXY_HTTP:
  case CURLPROXY_HTTP_1_0:
    /* do nothing here. handled later. */
    break;
  default:
    failf(data, "unknown proxytype option given");
    result = CURLE_COULDNT_CONNECT;
    break;
  }

  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
    /* BLOCKING */
    /* We want "seamless" FTP operations through HTTP proxy tunnel */

    /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
     * member conn->proto.http; we want FTP through HTTP and we have to
     * change the member temporarily for connecting to the HTTP proxy. After
     * Curl_proxyCONNECT we have to set back the member to the original
     * struct FTP pointer
     */
    struct HTTP http_proxy;
    struct FTP *ftp_save = data->req.protop;
    memset(&http_proxy, 0, sizeof(http_proxy));
    data->req.protop = &http_proxy;

    result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);

    data->req.protop = ftp_save;

    if(result)
      return result;

    if(conn->tunnel_state[SECONDARYSOCKET] != TUNNEL_COMPLETE) {
      /* the CONNECT procedure is not complete, the tunnel is not yet up */
      state(conn, FTP_STOP); /* this phase is completed */
      conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
      return result;
    }
    else
      *magicdone = TRUE;
  }
  return result;
}

static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
                                    int ftpcode)
                                    int ftpcode)
{
{
@@ -1812,13 +1885,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
  struct Curl_dns_entry *addr=NULL;
  struct Curl_dns_entry *addr=NULL;
  int rc;
  int rc;
  unsigned short connectport; /* the local port connect() should use! */
  unsigned short connectport; /* the local port connect() should use! */
  unsigned short newport=0; /* remote port */
  bool connected;
  bool connected;

  /* newhost must be able to hold a full IP-style address in ASCII, which
     in the IPv6 case means 5*8-1 = 39 letters */
#define NEWHOST_BUFSIZE 48
  char newhost[NEWHOST_BUFSIZE];
  char *str=&data->state.buffer[4];  /* start on the first letter */
  char *str=&data->state.buffer[4];  /* start on the first letter */


  if((ftpc->count1 == 0) &&
  if((ftpc->count1 == 0) &&
@@ -1851,7 +1918,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
          return CURLE_FTP_WEIRD_PASV_REPLY;
          return CURLE_FTP_WEIRD_PASV_REPLY;
        }
        }
        if(ptr) {
        if(ptr) {
          newport = (unsigned short)(num & 0xffff);
          ftpc->newport = (unsigned short)(num & 0xffff);


          if(conn->bits.tunnel_proxy ||
          if(conn->bits.tunnel_proxy ||
             conn->proxytype == CURLPROXY_SOCKS5 ||
             conn->proxytype == CURLPROXY_SOCKS5 ||
@@ -1860,10 +1927,11 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
             conn->proxytype == CURLPROXY_SOCKS4A)
             conn->proxytype == CURLPROXY_SOCKS4A)
            /* proxy tunnel -> use other host info because ip_addr_str is the
            /* proxy tunnel -> use other host info because ip_addr_str is the
               proxy address not the ftp host */
               proxy address not the ftp host */
            snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
            snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s",
                     conn->host.name);
          else
          else
            /* use the same IP we are already connected to */
            /* use the same IP we are already connected to */
            snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
            snprintf(ftpc->newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
        }
        }
      }
      }
      else
      else
@@ -1916,14 +1984,15 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
         conn->proxytype == CURLPROXY_SOCKS4A)
         conn->proxytype == CURLPROXY_SOCKS4A)
        /* proxy tunnel -> use other host info because ip_addr_str is the
        /* proxy tunnel -> use other host info because ip_addr_str is the
           proxy address not the ftp host */
           proxy address not the ftp host */
        snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
        snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s", conn->host.name);
      else
      else
        snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
        snprintf(ftpc->newhost, sizeof(ftpc->newhost), "%s",
                 conn->ip_addr_str);
    }
    }
    else
    else
      snprintf(newhost, sizeof(newhost),
      snprintf(ftpc->newhost, sizeof(ftpc->newhost),
               "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
               "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
    newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
    ftpc->newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
  }
  }
  else if(ftpc->count1 == 0) {
  else if(ftpc->count1 == 0) {
    /* EPSV failed, move on to PASV */
    /* EPSV failed, move on to PASV */
@@ -1957,15 +2026,15 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
  }
  }
  else {
  else {
    /* normal, direct, ftp connection */
    /* normal, direct, ftp connection */
    rc = Curl_resolv(conn, newhost, newport, &addr);
    rc = Curl_resolv(conn, ftpc->newhost, ftpc->newport, &addr);
    if(rc == CURLRESOLV_PENDING)
    if(rc == CURLRESOLV_PENDING)
      /* BLOCKING */
      /* BLOCKING */
      (void)Curl_resolver_wait_resolv(conn, &addr);
      (void)Curl_resolver_wait_resolv(conn, &addr);


    connectport = newport; /* we connect to the remote port */
    connectport = ftpc->newport; /* we connect to the remote port */


    if(!addr) {
    if(!addr) {
      failf(data, "Can't resolve new host %s:%hu", newhost, connectport);
      failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport);
      return CURLE_FTP_CANT_GET_HOST;
      return CURLE_FTP_CANT_GET_HOST;
    }
    }
  }
  }
@@ -1990,82 +2059,21 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
  /*
  /*
   * When this is used from the multi interface, this might've returned with
   * When this is used from the multi interface, this might've returned with
   * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
   * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
   * connect to connect and we should not be "hanging" here waiting.
   * connect to connect.
   */
   */


  if(data->set.verbose)
  if(data->set.verbose)
    /* this just dumps information about this second connection */
    /* this just dumps information about this second connection */
    ftp_pasv_verbose(conn, conninfo, newhost, connectport);
    ftp_pasv_verbose(conn, conninfo, ftpc->newhost, connectport);


  switch(conn->proxytype) {
  if(connected) {
    /* FIX: this MUST wait for a proper connect first if 'connected' is
    /* Only do the proxy connection magic if we're actually connected.  We do
     * FALSE */
       this little trick and send in the same 'connected' variable here again
  case CURLPROXY_SOCKS5:
       and it will be set FALSE by proxy_magic() for when for example the
  case CURLPROXY_SOCKS5_HOSTNAME:
       CONNECT procedure doesn't complete */
    result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
    infof(data, "Connection to proxy confirmed almost instantly\n");
                         SECONDARYSOCKET, conn);
    result = proxy_magic(conn, ftpc->newhost, ftpc->newport, &connected);
    connected = TRUE;
    break;
  case CURLPROXY_SOCKS4:
    result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
                         SECONDARYSOCKET, conn, FALSE);
    connected = TRUE;
    break;
  case CURLPROXY_SOCKS4A:
    result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
                         SECONDARYSOCKET, conn, TRUE);
    connected = TRUE;
    break;
  case CURLPROXY_HTTP:
  case CURLPROXY_HTTP_1_0:
    /* do nothing here. handled later. */
    break;
  default:
    failf(data, "unknown proxytype option given");
    result = CURLE_COULDNT_CONNECT;
    break;
  }

  if(result) {
    if(ftpc->count1 == 0 && ftpcode == 229)
      return ftp_epsv_disable(conn);
    return result;
  }

  if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
    /* FIX: this MUST wait for a proper connect first if 'connected' is
     * FALSE */

    /* BLOCKING */
    /* We want "seamless" FTP operations through HTTP proxy tunnel */

    /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
     * conn->proto.http; we want FTP through HTTP and we have to change the
     * member temporarily for connecting to the HTTP proxy. After
     * Curl_proxyCONNECT we have to set back the member to the original struct
     * FTP pointer
     */
    struct HTTP http_proxy;
    struct FTP *ftp_save = data->req.protop;
    memset(&http_proxy, 0, sizeof(http_proxy));
    data->req.protop = &http_proxy;

    result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);

    data->req.protop = ftp_save;

    if(result)
      return result;

    if(conn->tunnel_state[SECONDARYSOCKET] != TUNNEL_COMPLETE) {
      /* the CONNECT procedure is not complete, the tunnel is not yet up */
      state(conn, FTP_STOP); /* this phase is completed */
      conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;

      return result;
    }
  }
  }

  conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
  conn->bits.tcpconnect[SECONDARYSOCKET] = connected;
  conn->bits.do_more = TRUE;
  conn->bits.do_more = TRUE;
  state(conn, FTP_STOP); /* this phase is completed */
  state(conn, FTP_STOP); /* this phase is completed */
@@ -3625,6 +3633,10 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
    /* Ready to do more? */
    /* Ready to do more? */
    if(connected) {
    if(connected) {
      DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
      DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
      if(conn->bits.proxy) {
        infof(data, "Connection to proxy confirmed\n");
        result = proxy_magic(conn, ftpc->newhost, ftpc->newport, &connected);
      }
    }
    }
    else {
    else {
      if(result && (ftpc->count1 == 0)) {
      if(result && (ftpc->count1 == 0)) {
+6 −0
Original line number Original line Diff line number Diff line
@@ -147,6 +147,12 @@ struct ftp_conn {
  curl_off_t known_filesize; /* file size is different from -1, if wildcard
  curl_off_t known_filesize; /* file size is different from -1, if wildcard
                                LIST parsing was done and wc_statemach set
                                LIST parsing was done and wc_statemach set
                                it */
                                it */
  /* newhost must be able to hold a full IP-style address in ASCII, which
     in the IPv6 case means 5*8-1 = 39 letters */
#define NEWHOST_BUFSIZE 48
  char newhost[NEWHOST_BUFSIZE]; /* this is the pair to connect the DATA... */
  unsigned short newport;        /* connection to */

};
};


#define DEFAULT_ACCEPT_TIMEOUT   60000 /* milliseconds == one minute */
#define DEFAULT_ACCEPT_TIMEOUT   60000 /* milliseconds == one minute */
+4 −0
Original line number Original line Diff line number Diff line
@@ -129,6 +129,8 @@ CURLcode Curl_SOCKS4(const char *proxy_name,


  curlx_nonblock(sock, FALSE);
  curlx_nonblock(sock, FALSE);


  infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);

  /*
  /*
   * Compose socks4 request
   * Compose socks4 request
   *
   *
@@ -182,6 +184,8 @@ CURLcode Curl_SOCKS4(const char *proxy_name,
      else
      else
        hp = NULL; /* fail! */
        hp = NULL; /* fail! */


      infof(data, "SOCKS4 connect to %s (locally resolved)\n", buf);

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


    }
    }
+6 −3
Original line number Original line Diff line number Diff line
@@ -3219,9 +3219,12 @@ static CURLcode ConnectionStore(struct SessionHandle *data,
   Note: this function's sub-functions call failf()
   Note: this function's sub-functions call failf()


*/
*/
CURLcode Curl_connected_proxy(struct connectdata *conn)
CURLcode Curl_connected_proxy(struct connectdata *conn,
                              int sockindex)
{
{
  if(!conn->bits.proxy)
  if(!conn->bits.proxy || sockindex)
    /* this magic only works for the primary socket as the secondary is used
       for FTP only and it has FTP specific magic in ftp.c */
    return CURLE_OK;
    return CURLE_OK;


  switch(conn->proxytype) {
  switch(conn->proxytype) {
@@ -3281,7 +3284,7 @@ static CURLcode ConnectPlease(struct SessionHandle *data,
    conn->ip_addr = addr;
    conn->ip_addr = addr;


    if(*connected) {
    if(*connected) {
      result = Curl_connected_proxy(conn);
      result = Curl_connected_proxy(conn, FIRSTSOCKET);
      if(!result) {
      if(!result) {
        conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
        conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
        Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
        Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
Loading