aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ftp.c1054
1 files changed, 555 insertions, 499 deletions
diff --git a/lib/ftp.c b/lib/ftp.c
index 200cddc47..0e8758c1d 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -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)