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) |