diff options
| -rw-r--r-- | lib/ftp.c | 1054 | 
1 files changed, 555 insertions, 499 deletions
@@ -92,8 +92,8 @@  #endif  /* Local API functions */ -static CURLcode _ftp_sendquote(struct connectdata *conn, struct curl_slist *quote); -static CURLcode _ftp_cwd(struct connectdata *conn, char *path); +static CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote); +static CURLcode ftp_cwd(struct connectdata *conn, char *path);  /* easy-to-use macro: */  #define ftpsendf Curl_ftpsendf @@ -590,7 +590,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn)    /* Send any post-transfer QUOTE strings? */    if(data->set.postquote) { -    CURLcode result = _ftp_sendquote(conn, data->set.postquote); +    CURLcode result = ftp_sendquote(conn, data->set.postquote);      return result;    } @@ -599,7 +599,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn)  static  -CURLcode _ftp_sendquote(struct connectdata *conn, struct curl_slist *quote) +CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)  {    struct curl_slist *item;    ssize_t            nread; @@ -628,7 +628,7 @@ CURLcode _ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)  }  static  -CURLcode _ftp_cwd(struct connectdata *conn, char *path) +CURLcode ftp_cwd(struct connectdata *conn, char *path)  {    ssize_t nread;    int     ftpcode; @@ -648,7 +648,7 @@ CURLcode _ftp_cwd(struct connectdata *conn, char *path)  }  static -CURLcode _ftp_getfiletime(struct connectdata *conn, char *file) +CURLcode ftp_getfiletime(struct connectdata *conn, char *file)  {    CURLcode result=CURLE_OK;    int ftpcode; /* for ftp status */ @@ -683,7 +683,7 @@ CURLcode _ftp_getfiletime(struct connectdata *conn, char *file)    return  result;  } -static CURLcode _ftp_transfertype(struct connectdata *conn, +static CURLcode ftp_transfertype(struct connectdata *conn,                                    bool ascii)  {    struct SessionHandle *data = conn->data; @@ -707,7 +707,7 @@ static CURLcode _ftp_transfertype(struct connectdata *conn,  }  static -CURLcode _ftp_getsize(struct connectdata *conn, char *file, +CURLcode ftp_getsize(struct connectdata *conn, char *file,                        ssize_t *size)  {    struct SessionHandle *data = conn->data; @@ -741,12 +741,9 @@ CURLcode _ftp_getsize(struct connectdata *conn, char *file,   */  static void  ftp_pasv_verbose(struct connectdata *conn, -#ifdef ENABLE_IPV6 -                 struct addrinfo *newhost -#else -                 char *newhost /* ipv4 */ -#endif -) +                 Curl_addrinfo *addr, +                 char *newhost, /* ascii version */ +                 int port)  {  #ifndef ENABLE_IPV6    /***************************************************************** @@ -757,11 +754,10 @@ ftp_pasv_verbose(struct connectdata *conn,    struct in_addr in;    struct hostent * answer; -#if defined (HAVE_INET_NTOA_R) +#ifdef HAVE_INET_NTOA_R    char ntoa_buf[64];  #endif  #ifndef ENABLE_IPV6 -  struct sockaddr_in serv_addr;    char hostent_buf[8192];  #endif @@ -817,11 +813,11 @@ ftp_pasv_verbose(struct connectdata *conn,    infof(conn->data, "Connecting to %s (%s) port %u\n",          answer?answer->h_name:newhost,  #if defined(HAVE_INET_NTOA_R) -        inet_ntoa_r(in, ip_addr=ntoa_buf, sizeof(ntoa_buf)), +        inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),  #else -        ip_addr = inet_ntoa(in), +        inet_ntoa(in),  #endif -        connectport); +        port);  #else    /***************************************************************** @@ -836,13 +832,13 @@ ftp_pasv_verbose(struct connectdata *conn,  #else    const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;  #endif -  if (getnameinfo(newhost->ai_addr, newhost->ai_addrlen, +  if (getnameinfo(addr->ai_addr, addr->ai_addrlen,                    nbuf, sizeof(nbuf), sbuf, sizeof(sbuf), niflags)) {      snprintf(nbuf, sizeof(nbuf), "?");      snprintf(sbuf, sizeof(sbuf), "?");    } -  if (getnameinfo(newhost->ai_addr, newhost->ai_addrlen, +  if (getnameinfo(addr->ai_addr, addr->ai_addrlen,                    hbuf, sizeof(hbuf), NULL, 0, 0)) {      infof(conn->data, "Connecting to %s port %s\n", nbuf, sbuf);    }  @@ -850,570 +846,630 @@ ftp_pasv_verbose(struct connectdata *conn,      infof(conn->data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf);    }  #endif -  } +/********** + * PORT is the ftp client's way of telling the server that *WE* open a port + * that we listen on an awaits the server to connect to. This is the opposite + * of PASV. + */  static -CURLcode _ftp(struct connectdata *conn) +CURLcode ftp_use_port(struct connectdata *conn)  { -  /* this is FTP and no proxy */ -  ssize_t nread; -  CURLcode result;    struct SessionHandle *data=conn->data; -  char *buf = data->state.buffer; /* this is our buffer */ -  /* for the ftp PORT mode */    int portsock=-1; -  /* the ftp struct is already inited in ftp_connect() */ -  struct FTP *ftp = conn->proto.ftp; - -  long *bytecountp = ftp->bytecountp; -  int ftpcode; /* for ftp status */ - -  /* Send any QUOTE strings? */ -  if(data->set.quote) { -    if ((result = _ftp_sendquote(conn, data->set.quote)) != CURLE_OK) -      return result; -  } -     -  /* This is a re-used connection. Since we change directory to where the -     transfer is taking place, we must now get back to the original dir -     where we ended up after login: */ -  if (conn->bits.reuse) { -    if ((result = _ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) -      return result; -  } - -  /* change directory first! */ -  if(ftp->dir && ftp->dir[0]) { -    if ((result = _ftp_cwd(conn, ftp->dir)) != CURLE_OK) -        return result; -  } - -  /* Requested time of file? */ -  if(data->set.get_filetime && ftp->file) { -    result = _ftp_getfiletime(conn, ftp->file); -    if(result) -      return result; -  } - -  /* If we have selected NOBODY, it means that we only want file information. -     Which in FTP can't be much more than the file size! */ -  if(data->set.no_body) { -    /* The SIZE command is _not_ RFC 959 specified, and therefor many servers -       may not support it! It is however the only way we have to get a file's -       size! */ -    ssize_t filesize; - -    /* Some servers return different sizes for different modes, and thus we -       must set the proper type before we check the size */ -    result = _ftp_transfertype(conn, data->set.ftp_ascii); -    if(result) -      return result; - -    /* failing to get size is not a serious error */ -    result = _ftp_getsize(conn, ftp->file, &filesize); - -    if(CURLE_OK == result) { -      sprintf(buf, "Content-Length: %d\r\n", filesize); -      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); -      if(result) -        return result; -    } - -    /* If we asked for a time of the file and we actually got one as -       well, we "emulate" a HTTP-style header in our output. */ - -#ifdef HAVE_STRFTIME -    if(data->set.get_filetime && data->info.filetime) { -      struct tm *tm; -#ifdef HAVE_LOCALTIME_R -      struct tm buffer; -      tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); -#else -      tm = localtime(&data->info.filetime); -#endif -      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ -      strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", -               tm); -      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); -      if(result) -        return result; -    } -#endif - -    return CURLE_OK; -  } +  ssize_t nread; +  char *buf = data->state.buffer; /* this is our buffer */ +  int ftpcode; /* receive FTP response codes in this */ -  /* We have chosen to use the PORT command */ -  if(data->set.ftp_use_port) {  #ifdef ENABLE_IPV6 -    struct addrinfo hints, *res, *ai; -    struct sockaddr_storage ss; -    socklen_t sslen; -    char hbuf[NI_MAXHOST]; +  /****************************************************************** +   * +   * Here's a piece of IPv6-specific code coming up +   * +   */ -    struct sockaddr *sa=(struct sockaddr *)&ss; +  struct addrinfo hints, *res, *ai; +  struct sockaddr_storage ss; +  socklen_t sslen; +  char hbuf[NI_MAXHOST]; + +  struct sockaddr *sa=(struct sockaddr *)&ss;  #ifdef NI_WITHSCOPEID -    const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; +  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;  #else -    const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; +  const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;  #endif -    unsigned char *ap; -    unsigned char *pp; -    int alen, plen; -    char portmsgbuf[4096], tmp[4096]; +  unsigned char *ap; +  unsigned char *pp; +  int alen, plen; +  char portmsgbuf[4096], tmp[4096]; -    const char *mode[] = { "EPRT", "LPRT", "PORT", NULL }; -    char **modep; +  const char *mode[] = { "EPRT", "LPRT", "PORT", NULL }; +  char **modep; -    /* -     * we should use Curl_if2ip?  given pickiness of recent ftpd, -     * I believe we should use the same address as the control connection. -     */ -    sslen = sizeof(ss); -    if (getsockname(conn->firstsocket, (struct sockaddr *)&ss, &sslen) < 0) -      return CURLE_FTP_PORT_FAILED; - -    if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0, -                    niflags)) -      return CURLE_FTP_PORT_FAILED; - -    memset(&hints, 0, sizeof(hints)); -    hints.ai_family = sa->sa_family; -    /*hints.ai_family = ss.ss_family; -      this way can be used if sockaddr_storage is properly defined, as glibc  -      2.1.X doesn't do*/ -    hints.ai_socktype = SOCK_STREAM; -    hints.ai_flags = AI_PASSIVE; -    if (getaddrinfo(hbuf, "0", &hints, &res)) -      return CURLE_FTP_PORT_FAILED; - -    portsock = -1; -    for (ai = res; ai; ai = ai->ai_next) { -      portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); -      if (portsock < 0) -        continue; - -      if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) { -        sclose(portsock); -        portsock = -1; -        continue; -      } +  /* +   * we should use Curl_if2ip?  given pickiness of recent ftpd, +   * I believe we should use the same address as the control connection. +   */ +  sslen = sizeof(ss); +  if (getsockname(conn->firstsocket, (struct sockaddr *)&ss, &sslen) < 0) +    return CURLE_FTP_PORT_FAILED; +   +  if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0, +                  niflags)) +    return CURLE_FTP_PORT_FAILED; -      if (listen(portsock, 1) < 0) { -        sclose(portsock); -        portsock = -1; -        continue; -      } +  memset(&hints, 0, sizeof(hints)); +  hints.ai_family = sa->sa_family; +  /*hints.ai_family = ss.ss_family; +    this way can be used if sockaddr_storage is properly defined, as glibc  +    2.1.X doesn't do*/ +  hints.ai_socktype = SOCK_STREAM; +  hints.ai_flags = AI_PASSIVE; +  if (getaddrinfo(hbuf, "0", &hints, &res)) +    return CURLE_FTP_PORT_FAILED; +   +  portsock = -1; +  for (ai = res; ai; ai = ai->ai_next) { +    portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); +    if (portsock < 0) +      continue; -      break; +    if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) { +      sclose(portsock); +      portsock = -1; +      continue;      } -    if (portsock < 0) { -      failf(data, strerror(errno)); -      freeaddrinfo(res); -      return CURLE_FTP_PORT_FAILED; +       +    if (listen(portsock, 1) < 0) { +      sclose(portsock); +      portsock = -1; +      continue;      } +     +    break; +  } +  if (portsock < 0) { +    failf(data, strerror(errno)); +    freeaddrinfo(res); +    return CURLE_FTP_PORT_FAILED; +  } -    sslen = sizeof(ss); -    if (getsockname(portsock, sa, &sslen) < 0) { -      failf(data, strerror(errno)); -      freeaddrinfo(res); -      return CURLE_FTP_PORT_FAILED; -    } +  sslen = sizeof(ss); +  if (getsockname(portsock, sa, &sslen) < 0) { +    failf(data, strerror(errno)); +    freeaddrinfo(res); +    return CURLE_FTP_PORT_FAILED; +  } -    for (modep = (char **)mode; modep && *modep; modep++) { -      int lprtaf, eprtaf; - -      switch (sa->sa_family) { -      case AF_INET: -        ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr; -        alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr); -        pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port; -        plen = sizeof(((struct sockaddr_in *)&ss)->sin_port); -        lprtaf = 4; -        eprtaf = 1; -        break; -      case AF_INET6: -        ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr; -        alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr); -        pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port; -        plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port); -        lprtaf = 6; -        eprtaf = 2; -        break; -      default: -        ap = pp = NULL; -        lprtaf = eprtaf = -1; -        break; -      } +  for (modep = (char **)mode; modep && *modep; modep++) { +    int lprtaf, eprtaf; +     +    switch (sa->sa_family) { +    case AF_INET: +      ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr; +      alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr); +      pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port; +      plen = sizeof(((struct sockaddr_in *)&ss)->sin_port); +      lprtaf = 4; +      eprtaf = 1; +      break; +    case AF_INET6: +      ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr; +      alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr); +      pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port; +      plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port); +      lprtaf = 6; +      eprtaf = 2; +      break; +    default: +      ap = pp = NULL; +      lprtaf = eprtaf = -1; +      break; +    } -      if (strcmp(*modep, "EPRT") == 0) { -        if (eprtaf < 0) -          continue; -        if (getnameinfo((struct sockaddr *)&ss, sslen, -                         portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags)) -          continue; +    if (strcmp(*modep, "EPRT") == 0) { +      if (eprtaf < 0) +        continue; +      if (getnameinfo((struct sockaddr *)&ss, sslen, +                      portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags)) +        continue; -        /* do not transmit IPv6 scope identifier to the wire */ -      	if (sa->sa_family == AF_INET6) { -          char *q = strchr(portmsgbuf, '%'); +      /* do not transmit IPv6 scope identifier to the wire */ +      if (sa->sa_family == AF_INET6) { +        char *q = strchr(portmsgbuf, '%');            if (q)              *q = '\0'; -        } +      } -        ftpsendf(conn->firstsocket, conn, "%s |%d|%s|%s|", *modep, eprtaf, -                 portmsgbuf, tmp); -      } else if (strcmp(*modep, "LPRT") == 0 || -                 strcmp(*modep, "PORT") == 0) { -        int i; +      ftpsendf(conn->firstsocket, conn, "%s |%d|%s|%s|", *modep, eprtaf, +               portmsgbuf, tmp); +    } else if (strcmp(*modep, "LPRT") == 0 || +               strcmp(*modep, "PORT") == 0) { +      int i; +       +      if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0) +        continue; +      if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET) +        continue; -        if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0) +      portmsgbuf[0] = '\0'; +      if (strcmp(*modep, "LPRT") == 0) { +        snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen); +        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= +            sizeof(portmsgbuf)) {            continue; -        if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET) -          continue; - -        portmsgbuf[0] = '\0'; -        if (strcmp(*modep, "LPRT") == 0) { -          snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen); -          if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) { -            continue; -          } -        } - -        for (i = 0; i < alen; i++) { -          if (portmsgbuf[0]) -            snprintf(tmp, sizeof(tmp), ",%u", ap[i]); -          else -            snprintf(tmp, sizeof(tmp), "%u", ap[i]); - -          if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) { -            continue; -          }          } +      } -        if (strcmp(*modep, "LPRT") == 0) { -          snprintf(tmp, sizeof(tmp), ",%d", plen); - -          if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) -            continue; +      for (i = 0; i < alen; i++) { +        if (portmsgbuf[0]) +          snprintf(tmp, sizeof(tmp), ",%u", ap[i]); +        else +          snprintf(tmp, sizeof(tmp), "%u", ap[i]); +         +        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= +            sizeof(portmsgbuf)) { +          continue;          } +      } +       +      if (strcmp(*modep, "LPRT") == 0) { +        snprintf(tmp, sizeof(tmp), ",%d", plen); +         +        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) +          continue; +      } -        for (i = 0; i < plen; i++) { -          snprintf(tmp, sizeof(tmp), ",%u", pp[i]); - -          if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) { -              continue; -          } +      for (i = 0; i < plen; i++) { +        snprintf(tmp, sizeof(tmp), ",%u", pp[i]); +         +        if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= +            sizeof(portmsgbuf)) { +          continue;          } - -        ftpsendf(conn->firstsocket, conn, "%s %s", *modep, portmsgbuf);        } - -      nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); -      if(nread < 0) -        return CURLE_OPERATION_TIMEOUTED; - -      if (ftpcode != 200) { -        failf(data, "Server does not grok %s", *modep); -        continue; -      } else -        break; +       +      ftpsendf(conn->firstsocket, conn, "%s %s", *modep, portmsgbuf);      } - -    if (!*modep) { -      sclose(portsock); -      freeaddrinfo(res); -      return CURLE_FTP_PORT_FAILED; +     +    nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); +    if(nread < 0) +      return CURLE_OPERATION_TIMEOUTED; +     +    if (ftpcode != 200) { +      failf(data, "Server does not grok %s", *modep); +      continue;      } -    /* we set the secondary socket variable to this for now, it -       is only so that the cleanup function will close it in case -       we fail before the true secondary stuff is made */ -    conn->secondarysocket = portsock; - +    else +      break; +  } +   +  if (!*modep) { +    sclose(portsock); +    freeaddrinfo(res); +    return CURLE_FTP_PORT_FAILED; +  } +  /* we set the secondary socket variable to this for now, it +     is only so that the cleanup function will close it in case +     we fail before the true secondary stuff is made */ +  conn->secondarysocket = portsock; +    #else -    struct sockaddr_in sa; -    struct hostent *h=NULL; -    char *hostdataptr=NULL; -    size_t size; -    unsigned short porttouse; -    char myhost[256] = ""; - -    if(data->set.ftpport) { -      if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) { -        h = Curl_getaddrinfo(data, myhost, 0, &hostdataptr); -      } -      else { -        if(strlen(data->set.ftpport)>1) -          h = Curl_getaddrinfo(data, data->set.ftpport, 0, &hostdataptr); -        if(h) -          strcpy(myhost, data->set.ftpport); /* buffer overflow risk */ -      } +  /****************************************************************** +   * +   * Here's a piece of IPv4-specific code coming up +   * +   */ +  struct sockaddr_in sa; +  struct hostent *h=NULL; +  char *hostdataptr=NULL; +  size_t size; +  unsigned short porttouse; +  char myhost[256] = ""; + +  if(data->set.ftpport) { +    if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) { +      h = Curl_getaddrinfo(data, myhost, 0, &hostdataptr);      } -    if(! *myhost) { -      h=Curl_getaddrinfo(data, -                         getmyhost(myhost, sizeof(myhost)), -                         0, &hostdataptr); +    else { +      if(strlen(data->set.ftpport)>1) +        h = Curl_getaddrinfo(data, data->set.ftpport, 0, &hostdataptr); +      if(h) +        strcpy(myhost, data->set.ftpport); /* buffer overflow risk */      } -    infof(data, "We connect from %s\n", myhost); - -    if ( h ) { -      if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) { - -        /* we set the secondary socket variable to this for now, it -           is only so that the cleanup function will close it in case -           we fail before the true secondary stuff is made */ -        conn->secondarysocket = portsock; - -        memset((char *)&sa, 0, sizeof(sa)); -        memcpy((char *)&sa.sin_addr, -               h->h_addr, -               h->h_length); -        sa.sin_family = AF_INET; -        sa.sin_addr.s_addr = INADDR_ANY; -        sa.sin_port = 0; -        size = sizeof(sa); - -        if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) { -          /* we succeeded to bind */ -          struct sockaddr_in add; -          size = sizeof(add); - -          if(getsockname(portsock, (struct sockaddr *) &add, -                         (socklen_t *)&size)<0) { -            failf(data, "getsockname() failed"); -            return CURLE_FTP_PORT_FAILED; -          } -          porttouse = ntohs(add.sin_port); - -          if ( listen(portsock, 1) < 0 ) { -            failf(data, "listen(2) failed on socket"); -            free(hostdataptr); -            return CURLE_FTP_PORT_FAILED; -          } +  } +  if(! *myhost) { +    h=Curl_getaddrinfo(data, +                       getmyhost(myhost, sizeof(myhost)), +                       0, &hostdataptr); +  } +  infof(data, "We connect from %s\n", myhost); +   +  if ( h ) { +    if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) { +       +      /* we set the secondary socket variable to this for now, it +         is only so that the cleanup function will close it in case +         we fail before the true secondary stuff is made */ +      conn->secondarysocket = portsock; + +      memset((char *)&sa, 0, sizeof(sa)); +      memcpy((char *)&sa.sin_addr, +             h->h_addr, +             h->h_length); +      sa.sin_family = AF_INET; +      sa.sin_addr.s_addr = INADDR_ANY; +      sa.sin_port = 0; +      size = sizeof(sa); +       +      if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) { +        /* we succeeded to bind */ +        struct sockaddr_in add; +        size = sizeof(add); + +        if(getsockname(portsock, (struct sockaddr *) &add, +                       (socklen_t *)&size)<0) { +          failf(data, "getsockname() failed"); +          return CURLE_FTP_PORT_FAILED;          } -        else { -          failf(data, "bind(2) failed on socket"); +        porttouse = ntohs(add.sin_port); +         +        if ( listen(portsock, 1) < 0 ) { +          failf(data, "listen(2) failed on socket");            free(hostdataptr);            return CURLE_FTP_PORT_FAILED;          }        }        else { -        failf(data, "socket(2) failed (%s)"); +        failf(data, "bind(2) failed on socket");          free(hostdataptr);          return CURLE_FTP_PORT_FAILED;        } -      if(hostdataptr) -        /* free the memory used for name lookup */ -        free(hostdataptr);      }      else { -      failf(data, "could't find my own IP address (%s)", myhost); +      failf(data, "socket(2) failed (%s)"); +      free(hostdataptr);        return CURLE_FTP_PORT_FAILED;      } -    { -      struct in_addr in; -      unsigned short ip[5]; -      (void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr)); -#if defined (HAVE_INET_NTOA_R) -      /* ignore the return code from inet_ntoa_r() as it is int or -         char * depending on system */ -      inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)); -      sscanf( ntoa_buf, "%hu.%hu.%hu.%hu", -              &ip[0], &ip[1], &ip[2], &ip[3]); +    if(hostdataptr) +      /* free the memory used for name lookup */ +      free(hostdataptr); +  } +  else { +    failf(data, "could't find my own IP address (%s)", myhost); +    return CURLE_FTP_PORT_FAILED; +  } +  { +#ifdef HAVE_INET_NTOA_R +    char ntoa_buf[64]; +#endif +    struct in_addr in; +    unsigned short ip[5]; +    (void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr)); +#ifdef HAVE_INET_NTOA_R +    /* ignore the return code from inet_ntoa_r() as it is int or +       char * depending on system */ +    inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)); +    sscanf( ntoa_buf, "%hu.%hu.%hu.%hu", +            &ip[0], &ip[1], &ip[2], &ip[3]);  #else -      sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu", -              &ip[0], &ip[1], &ip[2], &ip[3]); +    sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu", +            &ip[0], &ip[1], &ip[2], &ip[3]);  #endif -      ftpsendf(conn->firstsocket, conn, "PORT %d,%d,%d,%d,%d,%d", -            ip[0], ip[1], ip[2], ip[3], -            porttouse >> 8, -            porttouse & 255); -    } +    ftpsendf(conn->firstsocket, conn, "PORT %d,%d,%d,%d,%d,%d", +             ip[0], ip[1], ip[2], ip[3], +             porttouse >> 8, +             porttouse & 255); +  } -    nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); -    if(nread < 0) -      return CURLE_OPERATION_TIMEOUTED; +  nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); +  if(nread < 0) +    return CURLE_OPERATION_TIMEOUTED; -    if(ftpcode != 200) { -      failf(data, "Server does not grok PORT, try without it!"); -      return CURLE_FTP_PORT_FAILED; -    }      -#endif /* ENABLE_IPV6 */ +  if(ftpcode != 200) { +    failf(data, "Server does not grok PORT, try without it!"); +    return CURLE_FTP_PORT_FAILED;    } -  else { /* we use the PASV command */ +#endif /* end of ipv4-specific code */ + +  return CURLE_OK; +} + +/********** + * PASV is the ftp client's way of asking the server to open a second port + * that we can connect to (for the data transfer). This is the opposite of + * PORT. + */ + +static +CURLcode ftp_use_pasv(struct connectdata *conn) +{ +  struct SessionHandle *data = conn->data; +  ssize_t nread; +  char *buf = data->state.buffer; /* this is our buffer */ +  int ftpcode; /* receive FTP response codes in this */ +  CURLcode result; +  #if 0 -    /* no support for IPv6 passive mode yet */ -    char *mode[] = { "EPSV", "LPSV", "PASV", NULL }; -    int results[] = { 229, 228, 227, 0 }; +  /* no support for IPv6 passive mode yet */ +  char *mode[] = { "EPSV", "LPSV", "PASV", NULL }; +  int results[] = { 229, 228, 227, 0 };  #else -    const char *mode[] = { "PASV", NULL }; -    int results[] = { 227, 0 }; +  const char *mode[] = { "PASV", NULL }; +  int results[] = { 227, 0 };  #endif -    int modeoff; - -    for (modeoff = 0; mode[modeoff]; modeoff++) { -      ftpsendf(conn->firstsocket, conn, mode[modeoff]); -      nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); -      if(nread < 0) -        return CURLE_OPERATION_TIMEOUTED; - +  int modeoff; +   +  for (modeoff = 0; mode[modeoff]; modeoff++) { +    ftpsendf(conn->firstsocket, conn, mode[modeoff]); +    nread = Curl_GetFTPResponse(conn->firstsocket, buf, conn, &ftpcode); +    if(nread < 0) +      return CURLE_OPERATION_TIMEOUTED; +            if (ftpcode == results[modeoff])          break; -    } - -    if (!mode[modeoff]) { -      failf(data, "Odd return code after PASV"); -      return CURLE_FTP_WEIRD_PASV_REPLY; -    } -    else if (strcmp(mode[modeoff], "PASV") == 0) { -      int ip[4]; -      int port[2]; -      unsigned short newport; /* remote port, not necessary the local one */ -      unsigned short connectport; /* the local port connect() should use! */ -      char newhost[32]; +  } -      Curl_addrinfo *addr; -      char *hostdataptr=NULL; +  if (!mode[modeoff]) { +    failf(data, "Odd return code after PASV"); +    return CURLE_FTP_WEIRD_PASV_REPLY; +  } +  else if (strcmp(mode[modeoff], "PASV") == 0) { +    int ip[4]; +    int port[2]; +    unsigned short newport; /* remote port, not necessary the local one */ +    unsigned short connectport; /* the local port connect() should use! */ +    char newhost[32]; +     +    Curl_addrinfo *addr; +    char *hostdataptr=NULL; -#ifndef ENABLE_IPV6 -      char *ip_addr; +#ifdef ENABLE_IPV6 +    struct addrinfo *ai;  #else -      struct addrinfo *ai; +    struct sockaddr_in serv_addr;  #endif -      char *str=buf; +    char *str=buf; -      /* -       * New 227-parser June 3rd 1999. -       * It now scans for a sequence of six comma-separated numbers and -       * will take them as IP+port indicators. -       * -       * Found reply-strings include: -       * "227 Entering Passive Mode (127,0,0,1,4,51)" -       * "227 Data transfer will passively listen to 127,0,0,1,4,51" -       * "227 Entering passive mode. 127,0,0,1,4,51" -       */ +    /* +     * New 227-parser June 3rd 1999. +     * It now scans for a sequence of six comma-separated numbers and +     * will take them as IP+port indicators. +     * +     * Found reply-strings include: +     * "227 Entering Passive Mode (127,0,0,1,4,51)" +     * "227 Data transfer will passively listen to 127,0,0,1,4,51" +     * "227 Entering passive mode. 127,0,0,1,4,51" +     */ -      while(*str) { -	      if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d", -                        &ip[0], &ip[1], &ip[2], &ip[3], -                        &port[0], &port[1])) -    	    break; -        str++; -      } +    while(*str) { +      if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d", +                      &ip[0], &ip[1], &ip[2], &ip[3], +                      &port[0], &port[1])) +        break; +      str++; +    } -      if(!*str) { -        failf(data, "Couldn't interpret this 227-reply: %s", buf); -        return CURLE_FTP_WEIRD_227_FORMAT; -      } +    if(!*str) { +      failf(data, "Couldn't interpret this 227-reply: %s", buf); +      return CURLE_FTP_WEIRD_227_FORMAT; +    } -      sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); -      newport = (port[0]<<8) + port[1]; -      if(data->change.proxy) { -        /* -         * This is a tunnel through a http proxy and we need to connect to the -         * proxy again here. We already have the name info for it since the -         * previous lookup. -         */ -        addr = conn->hp; -        connectport = -          (unsigned short)conn->port; /* we connect to the proxy's port */ -      } -      else { -        /* normal, direct, ftp connection */ -        addr = Curl_getaddrinfo(data, newhost, newport, &hostdataptr); -        if(!addr) { -          failf(data, "Can't resolve new host %s", newhost); -          return CURLE_FTP_CANT_GET_HOST; -        } -        connectport = newport; /* we connect to the remote port */ +    sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); +    newport = (port[0]<<8) + port[1]; +    if(data->change.proxy) { +      /* +       * This is a tunnel through a http proxy and we need to connect to the +       * proxy again here. We already have the name info for it since the +       * previous lookup. +       */ +      addr = conn->hp; +      connectport = +        (unsigned short)conn->port; /* we connect to the proxy's port */ +    } +    else { +      /* normal, direct, ftp connection */ +      addr = Curl_getaddrinfo(data, newhost, newport, &hostdataptr); +      if(!addr) { +        failf(data, "Can't resolve new host %s", newhost); +        return CURLE_FTP_CANT_GET_HOST;        } -	 +      connectport = newport; /* we connect to the remote port */ +    } +      #ifdef ENABLE_IPV6 -      conn->secondarysocket = -1; -      for (ai = addr; ai; ai = ai->ai_next) { -        /* XXX for now, we can do IPv4 only */ -        if (ai->ai_family != AF_INET) -          continue; - -        conn->secondarysocket = socket(ai->ai_family, ai->ai_socktype, -                                       ai->ai_protocol); -        if (conn->secondarysocket < 0) -          continue; - -        if (connect(conn->secondarysocket, ai->ai_addr, ai->ai_addrlen) < 0) { -          close(conn->secondarysocket); -          conn->secondarysocket = -1; -          continue; -        } - -      	if(data->set.verbose) -          /* this just dumps information about this second connection */ -          ftp_pasv_verbose(conn, ai); -      	break; +    conn->secondarysocket = -1; +    for (ai = addr; ai; ai = ai->ai_next) { +      /* XXX for now, we can do IPv4 only */ +      if (ai->ai_family != AF_INET) +        continue; +       +      conn->secondarysocket = socket(ai->ai_family, ai->ai_socktype, +                                     ai->ai_protocol); +      if (conn->secondarysocket < 0) +        continue; +       +      if (connect(conn->secondarysocket, ai->ai_addr, ai->ai_addrlen) < 0) { +        close(conn->secondarysocket); +        conn->secondarysocket = -1; +        continue;        } -      if (conn->secondarysocket < 0) { -        failf(data, strerror(errno)); -        return CURLE_FTP_CANT_RECONNECT; -      } +      if(data->set.verbose) +        /* this just dumps information about this second connection */ +        ftp_pasv_verbose(conn, ai, newhost, 0 /* port not really known */); +      break; +    } + +    if (conn->secondarysocket < 0) { +      failf(data, strerror(errno)); +      return CURLE_FTP_CANT_RECONNECT; +    }  #else -      /* IPv4 code */ -      conn->secondarysocket = socket(AF_INET, SOCK_STREAM, 0); +    /* IPv4 code */ +    conn->secondarysocket = socket(AF_INET, SOCK_STREAM, 0); -      memset((char *) &serv_addr, '\0', sizeof(serv_addr)); -      memcpy((char *)&(serv_addr.sin_addr), addr->h_addr, addr->h_length); -      serv_addr.sin_family = addr->h_addrtype; +    memset((char *) &serv_addr, '\0', sizeof(serv_addr)); +    memcpy((char *)&(serv_addr.sin_addr), addr->h_addr, addr->h_length); +    serv_addr.sin_family = addr->h_addrtype; -      serv_addr.sin_port = htons(connectport); +    serv_addr.sin_port = htons(connectport); -      if(data->set.verbose) -        /* this just dumps information about this second connection */ -        ftp_pasv_verbose(conn, newhost); +    if(data->set.verbose) +      /* this just dumps information about this second connection */ +      ftp_pasv_verbose(conn, addr, newhost, connectport); -      if(hostdataptr) -        free(hostdataptr); - -      if (connect(conn->secondarysocket, (struct sockaddr *) &serv_addr, -                  sizeof(serv_addr)) < 0) { -        switch(errno) { +    if(hostdataptr) +      free(hostdataptr); +     +    if (connect(conn->secondarysocket, (struct sockaddr *) &serv_addr, +                sizeof(serv_addr)) < 0) { +      switch(errno) {  #ifdef ECONNREFUSED -          /* this should be made nicer */ -        case ECONNREFUSED: -          failf(data, "Connection refused by ftp server"); -          break; +        /* this should be made nicer */ +      case ECONNREFUSED: +        failf(data, "Connection refused by ftp server"); +        break;  #endif  #ifdef EINTR -        case EINTR: -          failf(data, "Connection timed out to ftp server"); -          break; +      case EINTR: +        failf(data, "Connection timed out to ftp server"); +        break;  #endif -        default: -          failf(data, "Can't connect to ftp server"); -          break; -        } -        return CURLE_FTP_CANT_RECONNECT; +      default: +        failf(data, "Can't connect to ftp server"); +        break;        } +      return CURLE_FTP_CANT_RECONNECT; +    }  #endif /* end of IPv4-specific code*/ -      if (data->set.tunnel_thru_httpproxy) { -        /* We want "seamless" FTP operations through HTTP proxy tunnel */ -        result = Curl_ConnectHTTPProxyTunnel(conn, conn->secondarysocket, -                                             newhost, newport); -        if(CURLE_OK != result) -          return result; -      } -    } else { -      return CURLE_FTP_CANT_RECONNECT; +    if (data->set.tunnel_thru_httpproxy) { +      /* We want "seamless" FTP operations through HTTP proxy tunnel */ +      result = Curl_ConnectHTTPProxyTunnel(conn, conn->secondarysocket, +                                           newhost, newport); +      if(CURLE_OK != result) +        return result; +    } +  } +  else +    return CURLE_FTP_CANT_RECONNECT; + +  return CURLE_OK; +} + + +static +CURLcode ftp_perform(struct connectdata *conn) +{ +  /* this is FTP and no proxy */ +  ssize_t nread; +  CURLcode result; +  struct SessionHandle *data=conn->data; +  char *buf = data->state.buffer; /* this is our buffer */ + +  /* the ftp struct is already inited in ftp_connect() */ +  struct FTP *ftp = conn->proto.ftp; + +  long *bytecountp = ftp->bytecountp; +  int ftpcode; /* for ftp status */ + +  /* Send any QUOTE strings? */ +  if(data->set.quote) { +    if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK) +      return result; +  } +     +  /* This is a re-used connection. Since we change directory to where the +     transfer is taking place, we must now get back to the original dir +     where we ended up after login: */ +  if (conn->bits.reuse) { +    if ((result = ftp_cwd(conn, ftp->entrypath)) != CURLE_OK) +      return result; +  } + +  /* change directory first! */ +  if(ftp->dir && ftp->dir[0]) { +    if ((result = ftp_cwd(conn, ftp->dir)) != CURLE_OK) +        return result; +  } + +  /* Requested time of file? */ +  if(data->set.get_filetime && ftp->file) { +    result = ftp_getfiletime(conn, ftp->file); +    if(result) +      return result; +  } + +  /* If we have selected NOBODY, it means that we only want file information. +     Which in FTP can't be much more than the file size! */ +  if(data->set.no_body) { +    /* The SIZE command is _not_ RFC 959 specified, and therefor many servers +       may not support it! It is however the only way we have to get a file's +       size! */ +    ssize_t filesize; + +    /* Some servers return different sizes for different modes, and thus we +       must set the proper type before we check the size */ +    result = ftp_transfertype(conn, data->set.ftp_ascii); +    if(result) +      return result; + +    /* failing to get size is not a serious error */ +    result = ftp_getsize(conn, ftp->file, &filesize); + +    if(CURLE_OK == result) { +      sprintf(buf, "Content-Length: %d\r\n", filesize); +      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); +      if(result) +        return result;      } + +    /* If we asked for a time of the file and we actually got one as +       well, we "emulate" a HTTP-style header in our output. */ + +#ifdef HAVE_STRFTIME +    if(data->set.get_filetime && data->info.filetime) { +      struct tm *tm; +#ifdef HAVE_LOCALTIME_R +      struct tm buffer; +      tm = (struct tm *)localtime_r(&data->info.filetime, &buffer); +#else +      tm = localtime(&data->info.filetime); +#endif +      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ +      strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S %Z\r\n", +               tm); +      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0); +      if(result) +        return result; +    } +#endif + +    return CURLE_OK;    } -  /* we have the (new) data connection ready */ + +  /* Get us a second connection up and connected */ +  if(data->set.ftp_use_port) +    /* We have chosen to use the PORT command */ +    result = ftp_use_port(conn); +  else +    /* We have chosen (this is default) to use the PASV command */ +    result = ftp_use_pasv(conn); + +  if(result) +    return result; + +  /* we have the data connection ready */    infof(data, "Connected the data stream!\n");    if(data->set.upload) {      /* Set type to binary (unless specified ASCII) */ -    result = _ftp_transfertype(conn, data->set.ftp_ascii); +    result = ftp_transfertype(conn, data->set.ftp_ascii);      if(result)        return result; @@ -1435,7 +1491,7 @@ CURLcode _ftp(struct connectdata *conn)          /* we could've got a specified offset from the command line,             but now we know we didn't */ -        if(CURLE_OK != _ftp_getsize(conn, ftp->file, &conn->resume_from)) { +        if(CURLE_OK != ftp_getsize(conn, ftp->file, &conn->resume_from)) {            failf(data, "Couldn't get remote file size");            return CURLE_FTP_COULDNT_GET_SIZE;          } @@ -1510,7 +1566,7 @@ CURLcode _ftp(struct connectdata *conn)      if(data->set.ftp_use_port) {        /* PORT means we are now awaiting the server to connect to us. */ -      result = AllowServerConnect(data, conn, portsock); +      result = AllowServerConnect(data, conn, conn->secondarysocket);        if( result )          return result;      } @@ -1578,7 +1634,7 @@ CURLcode _ftp(struct connectdata *conn)        dirlist = TRUE;        /* Set type to ASCII */ -      result = _ftp_transfertype(conn, TRUE /* ASCII enforced */); +      result = ftp_transfertype(conn, TRUE /* ASCII enforced */);        if(result)          return result; @@ -1592,7 +1648,7 @@ CURLcode _ftp(struct connectdata *conn)      }      else {        /* Set type to binary (unless specified ASCII) */ -      result = _ftp_transfertype(conn, data->set.ftp_ascii); +      result = ftp_transfertype(conn, data->set.ftp_ascii);        if(result)          return result; @@ -1605,7 +1661,7 @@ CURLcode _ftp(struct connectdata *conn)           * the best way to know if we're trying to resume beyond the EOF.  */          int foundsize=-1; -        result = _ftp_getsize(conn, ftp->file, &foundsize); +        result = ftp_getsize(conn, ftp->file, &foundsize);          if(CURLE_OK != result) {            infof(data, "ftp server doesn't support SIZE\n"); @@ -1736,7 +1792,7 @@ CURLcode _ftp(struct connectdata *conn)          size = downloadsize;        if(data->set.ftp_use_port) { -        result = AllowServerConnect(data, conn, portsock); +        result = AllowServerConnect(data, conn, conn->secondarysocket);          if( result )            return result;        } @@ -1813,7 +1869,7 @@ CURLcode Curl_ftp(struct connectdata *conn)    else      ftp->dir = NULL; -  retcode = _ftp(conn); +  retcode = ftp_perform(conn);    /* clean up here, success or error doesn't matter */    if(ftp->file)  | 
