Alexander Krasnostavsky's FTP third party transfer (proxy) support

This commit is contained in:
Daniel Stenberg 2004-06-03 11:41:05 +00:00
parent 7dcb102733
commit ea81dd9e2e
8 changed files with 583 additions and 230 deletions

561
lib/ftp.c
View File

@ -1,8 +1,8 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
@ -10,7 +10,7 @@
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
@ -119,6 +119,10 @@ static CURLcode ftp_cwd(struct connectdata *conn, char *path);
static CURLcode ftp_mkd(struct connectdata *conn, char *path);
static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path);
static CURLcode ftp_quit(struct connectdata *conn);
static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn);
static CURLcode ftp_3rdparty_transfer(struct connectdata *conn);
static CURLcode ftp_regular_transfer(struct connectdata *conn);
static CURLcode ftp_3rdparty(struct connectdata *conn);
/* easy-to-use macro: */
#define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
@ -161,11 +165,11 @@ static CURLcode AllowServerConnect(struct connectdata *conn)
long timespent = Curl_tvdiff(Curl_tvnow(), now)/1000;
long timeout = data->set.connecttimeout?data->set.connecttimeout:
(data->set.timeout?data->set.timeout: 0);
FD_ZERO(&rdset);
FD_SET(sock, &rdset);
if(timeout) {
timeout -= timespent;
if(timeout<=0) {
@ -369,7 +373,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
/* output debug output if that is requested */
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline);
Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, conn->host.dispname);
/*
* We pass all response-lines to the callback function registered
@ -380,7 +384,7 @@ CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
line_start, perline);
if(result)
return result;
#define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
isdigit((int)line[2]) && (' ' == line[3]))
@ -560,7 +564,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
}
}
}
/* send USER */
FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
@ -592,7 +596,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
else if(ftpcode == 230) {
/* 230 User ... logged in.
(user successfully logged in) */
infof(data, "We have successfully logged in\n");
}
else {
@ -611,7 +615,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
*/
if(conn->sec_complete)
Curl_sec_set_protection_level(conn);
/* We may need to issue a KAUTH here to have access to the files
* do it if user supplied a password
*/
@ -638,7 +642,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
exchange (the TLS negotiation in this case)
... (and on page 8):
Thus the PBSZ command must still be issued, but must have a parameter
of '0' to indicate that no buffering is taking place and the data
connection should not be encapsulated.
@ -659,7 +663,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
if(ftpcode == 200)
/* We have enabled SSL for the data connection! */
conn->ssl[SECONDARYSOCKET].use = TRUE;
@ -684,7 +688,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn)
if(!dir)
return CURLE_OUT_OF_MEMORY;
/* Reply format is like
257<space>"<directory-name>"<space><commentary> and the RFC959 says
@ -822,7 +826,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
ftp->response_time = 3600; /* set this back to one hour waits */
if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
failf(data, "control connection looks dead");
return result;
@ -842,10 +846,15 @@ CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
/* clear these for next connection */
ftp->no_transfer = FALSE;
ftp->dont_check = FALSE;
ftp->dont_check = FALSE;
if (!result && conn->sec_conn) { /* 3rd party transfer */
/* "done" with the secondary connection */
result = Curl_ftp_done(conn->sec_conn, status);
}
/* Send any post-transfer QUOTE strings? */
if(!result && data->set.postquote)
if(!status && !result && data->set.postquote)
result = ftp_sendquote(conn, data->set.postquote);
return result;
@ -859,7 +868,7 @@ CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
* The quote list is passed as an argument.
*/
static
static
CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
{
struct curl_slist *item;
@ -958,7 +967,7 @@ static CURLcode ftp_transfertype(struct connectdata *conn,
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
if(ftpcode != 200) {
failf(data, "Couldn't set %s mode",
ascii?"ASCII":"binary");
@ -1052,7 +1061,7 @@ ftp_pasv_verbose(struct connectdata *conn,
/* Bjorn Reese (November 28 2001):
The Tru64 man page on gethostbyaddr_r() says that
the hostent struct must be filled with zeroes before the call to
gethostbyaddr_r().
gethostbyaddr_r().
... as must be struct hostent_data Craig Markwardt 19 Sep 2002. */
@ -1065,7 +1074,7 @@ ftp_pasv_verbose(struct connectdata *conn,
answer=NULL;
else
answer=(struct hostent *)hostent_buf;
# endif
# ifdef HAVE_GETHOSTBYADDR_R_7
/* Solaris and IRIX */
@ -1085,7 +1094,7 @@ ftp_pasv_verbose(struct connectdata *conn,
&h_errnop))
answer=NULL; /* error */
# endif
# else
(void)hostent_buf; /* avoid compiler warning */
answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
@ -1117,7 +1126,7 @@ ftp_pasv_verbose(struct connectdata *conn,
snprintf(nbuf, sizeof(nbuf), "?");
snprintf(sbuf, sizeof(sbuf), "?");
}
if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
hbuf, sizeof(hbuf), NULL, 0, 0)) {
infof(conn->data, "Connecting to %s (%s) port %s\n", nbuf, newhost, sbuf);
@ -1178,7 +1187,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
failf(data, "getsockname() returned %d\n", rc);
return CURLE_FTP_PORT_FAILED;
}
rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
NIFLAGS);
if(rc) {
@ -1189,7 +1198,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
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
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;
@ -1199,7 +1208,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
failf(data, "getaddrinfo() returned %d\n", rc);
return CURLE_FTP_PORT_FAILED;
}
portsock = CURL_SOCKET_BAD;
error = 0;
for (ai = res; ai; ai = ai->ai_next) {
@ -1221,14 +1230,14 @@ CURLcode ftp_use_port(struct connectdata *conn)
portsock = CURL_SOCKET_BAD;
continue;
}
if (listen(portsock, 1) < 0) {
error = Curl_ourerrno();
sclose(portsock);
portsock = CURL_SOCKET_BAD;
continue;
}
break;
}
freeaddrinfo(res);
@ -1247,7 +1256,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
modep && *modep; modep++) {
int lprtaf, eprtaf;
int alen=0, plen=0;
switch (sa->sa_family) {
case AF_INET:
ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
@ -1314,45 +1323,45 @@ CURLcode ftp_use_port(struct connectdata *conn)
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;
}
}
result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
if(result)
return result;
}
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
if (ftpcode != 200) {
continue;
}
else
break;
}
if (!*modep) {
sclose(portsock);
failf(data, "PORT command attempts failed");
@ -1362,7 +1371,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
is only so that the cleanup function will close it in case
we fail before the true secondary stuff is made */
conn->sock[SECONDARYSOCKET] = portsock;
#else
/******************************************************************
*
@ -1403,7 +1412,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
/* pick a suitable default here */
socklen_t sslen;
sslen = sizeof(sa);
if (getsockname(conn->sock[FIRSTSOCKET],
(struct sockaddr *)&sa, &sslen) < 0) {
@ -1421,7 +1430,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
if ( h || sa_filled_in) {
if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) != CURL_SOCKET_BAD ) {
int size;
/* 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 */
@ -1438,7 +1447,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
sa.sin_port = 0;
size = sizeof(sa);
if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
/* we succeeded to bind */
struct sockaddr_in add;
@ -1450,7 +1459,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
return CURLE_FTP_PORT_FAILED;
}
porttouse = ntohs(add.sin_port);
if ( listen(portsock, 1) < 0 ) {
failf(data, "listen(2) failed on socket");
return CURLE_FTP_PORT_FAILED;
@ -1492,7 +1501,7 @@ CURLcode ftp_use_port(struct connectdata *conn)
#endif
infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
ip[0], ip[1], ip[2], ip[3], porttouse);
result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
ip[0], ip[1], ip[2], ip[3],
porttouse >> 8,
@ -1555,12 +1564,12 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
int modeoff;
unsigned short connectport; /* the local port connect() should use! */
unsigned short newport=0; /* remote port, not necessary the local one */
/* newhost must be able to hold a full IP-style address in ASCII, which
in the IPv6 case means 5*8-1 = 39 letters */
char newhost[48];
char *newhostp=NULL;
for (modeoff = (data->set.ftp_use_epsv?0:1);
mode[modeoff]; modeoff++) {
result = Curl_ftpsendf(conn, "%s", mode[modeoff]);
@ -1592,7 +1601,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
* "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],
@ -1639,7 +1648,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
/* we should use the same host we already are connected to */
newhostp = conn->host.name;
}
}
}
else
ptr=NULL;
}
@ -1664,7 +1673,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
rc = Curl_wait_for_resolv(conn, &addr);
connectport =
(unsigned short)conn->port; /* we connect to the proxy's port */
(unsigned short)conn->port; /* we connect to the proxy's port */
}
else {
@ -1679,7 +1688,7 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
}
connectport = newport; /* we connect to the remote port */
}
result = Curl_connecthost(conn,
addr,
connectport,
@ -1697,11 +1706,11 @@ CURLcode ftp_use_pasv(struct connectdata *conn,
* the 'connected' set to FALSE and thus we are now awaiting a non-blocking
* connect to connect and we should not be "hanging" here waiting.
*/
if(data->set.verbose)
/* this just dumps information about this second connection */
ftp_pasv_verbose(conn, conninfo, newhostp, connectport);
if(conn->bits.tunnel_proxy) {
/* We want "seamless" FTP operations through HTTP proxy tunnel */
result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
@ -1809,10 +1818,10 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
/* no data to transfer */
result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
/* Set no_transfer so that we won't get any error in
* Curl_ftp_done() because we didn't transfer anything! */
ftp->no_transfer = TRUE;
ftp->no_transfer = TRUE;
return CURLE_OK;
}
@ -1867,7 +1876,7 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
SECONDARYSOCKET, bytecountp);
if(result)
return result;
}
else if(!conn->bits.no_body) {
/* Retrieve file or directory */
@ -2016,7 +2025,7 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
ftp->no_transfer = TRUE;
return CURLE_OK;
}
/* Set resume file transfer offset */
infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
"\n",
@ -2047,16 +2056,16 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
A;
150 Opening BINARY mode data connection for /etc/passwd (2241
bytes). (ok, the file is being transfered)
B:
150 Opening ASCII mode data connection for /bin/ls
150 Opening ASCII mode data connection for /bin/ls
C:
150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
D:
150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
E:
125 Data connection already open; Transfer starting. */
@ -2104,7 +2113,7 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
/* get the number! */
size = curlx_strtoofft(bytes, NULL, 0);
}
}
}
else if(downloadsize > -1)
@ -2147,7 +2156,7 @@ CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
return CURLE_FTP_COULDNT_RETR_FILE;
}
}
}
/* end of transfer */
@ -2249,7 +2258,7 @@ CURLcode ftp_perform(struct connectdata *conn,
int ftpcode;
ftp->no_transfer = TRUE; /* this means no actual transfer is made */
/* 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);
@ -2322,7 +2331,7 @@ CURLcode ftp_perform(struct connectdata *conn,
if(CURLE_OK == result && *connected)
infof(data, "Connected the data stream with PASV!\n");
}
return result;
}
@ -2334,116 +2343,15 @@ CURLcode ftp_perform(struct connectdata *conn,
* parts etc as a wrapper to the actual DO function (ftp_perform).
*
* The input argument is already checked for validity.
*
* ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
* Curl_ftp_done() function without finding any major problem.
*/
CURLcode Curl_ftp(struct connectdata *conn)
{
CURLcode retcode=CURLE_OK;
bool connected=0;
struct SessionHandle *data = conn->data;
struct FTP *ftp;
CURLcode retcode = CURLE_OK;
char *slash_pos; /* position of the first '/' char in curpos */
char *cur_pos=conn->path; /* current position in ppath. point at the begin
of next path component */
/* the ftp struct is already inited in ftp_connect() */
ftp = conn->proto.ftp;
ftp->ctl_valid = FALSE;
conn->size = -1; /* make sure this is unknown at this point */
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
Curl_pgrsSetUploadSize(data, 0);
Curl_pgrsSetDownloadSize(data, 0);
ftp->dirdepth = 0;
ftp->diralloc = 5; /* default dir depth to allocate */
ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0]));
if(!ftp->dirs)
return CURLE_OUT_OF_MEMORY;
ftp->dirs[0] = NULL; /* to start with */
/* parse the URL path into separate path components */
while((slash_pos=strchr(cur_pos, '/'))) {
/* 1 or 0 to indicate absolute directory */
bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 0);
/* seek out the next path component */
if (slash_pos-cur_pos) {
/* we skip empty path components, like "x//y" since the FTP command CWD
requires a parameter and a non-existant parameter a) doesn't work on
many servers and b) has no effect on the others. */
ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir,
slash_pos - cur_pos +
absolute_dir);
if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
failf(data, "no memory");
freedirs(ftp);
return CURLE_OUT_OF_MEMORY;
}
}
else {
cur_pos = slash_pos + 1; /* jump to the rest of the string */
continue;
}
if(!retcode) {
cur_pos = slash_pos + 1; /* jump to the rest of the string */
if(++ftp->dirdepth >= ftp->diralloc) {
/* enlarge array */
char *bigger;
ftp->diralloc *= 2; /* double the size each time */
bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0]));
if(!bigger) {
freedirs(ftp);
return CURLE_OUT_OF_MEMORY;
}
ftp->dirs = (char **)bigger;
}
}
}
ftp->file = cur_pos; /* the rest is the file name */
if(*ftp->file) {
ftp->file = curl_unescape(ftp->file, 0);
if(NULL == ftp->file) {
freedirs(ftp);
failf(data, "no memory");
return CURLE_OUT_OF_MEMORY;
}
}
if (conn->sec_conn) /* 3rd party transfer */
retcode = ftp_3rdparty(conn);
else
ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
pointer */
retcode = ftp_perform(conn, &connected);
if(CURLE_OK == retcode) {
if(connected)
retcode = Curl_ftp_nextconnect(conn);
if(retcode && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
/* Failure detected, close the second socket if it was created already */
sclose(conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
}
if(ftp->no_transfer)
/* no data to transfer */
retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
else if(!connected)
/* since we didn't connect now, we want do_more to get called */
conn->bits.do_more = TRUE;
}
else
freedirs(ftp);
ftp->ctl_valid = TRUE; /* seems good */
retcode = ftp_regular_transfer(conn);
return retcode;
}
@ -2470,7 +2378,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn,
va_start(ap, fmt);
vsnprintf(s, 250, fmt, ap);
va_end(ap);
strcat(s, "\r\n"); /* append a trailing CRLF */
bytes_written=0;
@ -2484,7 +2392,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn,
break;
if(conn->data->set.verbose)
Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written);
Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn->host.dispname);
if(bytes_written != (ssize_t)write_len) {
write_len -= bytes_written;
@ -2502,7 +2410,7 @@ CURLcode Curl_ftpsendf(struct connectdata *conn,
* ftp_quit()
*
* This should be called before calling sclose() on an ftp control connection
* (not data connections). We should then wait for the response from the
* (not data connections). We should then wait for the response from the
* server before returning. The calling code should then try to close the
* connection.
*
@ -2576,7 +2484,7 @@ static CURLcode ftp_mkd(struct connectdata *conn, char *path)
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if(result)
return result;
switch(ftpcode) {
case 257:
/* success! */
@ -2604,13 +2512,13 @@ static CURLcode ftp_mkd(struct connectdata *conn, char *path)
*
* This function does NOT call failf().
*/
static
static
CURLcode ftp_cwd(struct connectdata *conn, char *path)
{
ssize_t nread;
int ftpcode;
CURLcode result;
FTPSENDF(conn, "CWD %s", path);
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if (!result) {
@ -2635,7 +2543,7 @@ CURLcode ftp_cwd(struct connectdata *conn, char *path)
static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
{
CURLcode result;
result = ftp_cwd(conn, path);
if (result) {
if(conn->data->set.ftp_create_missing_dirs) {
@ -2651,4 +2559,307 @@ static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
return result;
}
/***********************************************************************
*
* ftp_3rdparty_pretransfer()
*
* Preparation for 3rd party transfer.
*
*/
static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
struct connectdata *sec_conn = conn->sec_conn;
/* sets transfer type */
result = ftp_transfertype(conn, data->set.ftp_ascii);
if (result)
return result;
result = ftp_transfertype(sec_conn, data->set.ftp_ascii);
if (result)
return result;
/* Send any PREQUOTE strings after transfer type is set? */
if (data->set.source_prequote) {
/* sends command(s) to source server before file transfer */
result = ftp_sendquote(sec_conn, data->set.source_prequote);
}
if (!result && data->set.prequote)
result = ftp_sendquote(conn, data->set.prequote);
return result;
}
/***********************************************************************
*
* ftp_3rdparty_transfer()
*
* Performs 3rd party transfer.
*
*/
static CURLcode ftp_3rdparty_transfer(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
ssize_t nread;
int ftpcode, ip[4], port[2];
struct SessionHandle *data = conn->data;
struct connectdata *sec_conn = conn->sec_conn;
char *buf = data->state.buffer; /* this is our buffer */
char *str = buf;
char pasv_port[50];
const char *stor_cmd;
struct connectdata *pasv_conn;
struct connectdata *port_conn;
if (data->set.pasvHost == CURL_TARGET_PASV) {
pasv_conn = conn;
port_conn = sec_conn;
}
else {
pasv_conn = sec_conn;
port_conn = conn;
}
/* sets the passive mode */
FTPSENDF(pasv_conn, "%s", "PASV");
result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
if (result) return result;
if (ftpcode != 227) {
failf(data, "Odd return code after PASV:%s", buf + 3);
return CURLE_FTP_WEIRD_PASV_REPLY;
}
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(pasv_conn->data, "Couldn't interpret this 227-reply: %s", buf);
return CURLE_FTP_WEIRD_227_FORMAT;
}
snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1],
ip[2], ip[3], port[0], port[1]);
/* sets data connection between remote hosts */
FTPSENDF(port_conn, "PORT %s", pasv_port);
result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
if (result)
return result;
if (ftpcode != 200) {
failf(data, "PORT command attempts failed:%s", buf + 3);
return CURLE_FTP_PORT_FAILED;
}
/* we might append onto the file instead of overwriting it */
stor_cmd = data->set.ftp_append?"APPE":"STOR";
/* transfers file between remote hosts */
FTPSENDF(sec_conn, "RETR %s", data->set.source_path);
if(data->set.pasvHost == CURL_TARGET_PASV) {
result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
if (result)
return result;
if (ftpcode != 150) {
failf(data, "Failed RETR: %s", buf + 4);
return CURLE_FTP_COULDNT_RETR_FILE;
}
result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
if(CURLE_OK == result)
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if (result)
return result;
if (ftpcode != 150) {
failf(data, "Failed FTP upload: %s", buf + 4);
return CURLE_FTP_COULDNT_STOR_FILE;
}
}
else {
result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
if(CURLE_OK == result)
result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
if (result)
return result;
if (ftpcode != 150) {
failf(data, "Failed FTP upload: %s", buf + 4);
return CURLE_FTP_COULDNT_STOR_FILE;
}
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
if (result)
return result;
if (ftpcode != 150) {
failf(data, "Failed FTP upload: %s", buf + 4);
return CURLE_FTP_COULDNT_STOR_FILE;
}
}
return CURLE_OK;
}
/***********************************************************************
*
* ftp_regular_transfer()
*
* The input argument is already checked for validity.
* Performs a regular transfer between local and remote hosts.
*
* ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
* Curl_ftp_done() function without finding any major problem.
*/
static
CURLcode ftp_regular_transfer(struct connectdata *conn)
{
CURLcode retcode=CURLE_OK;
bool connected=0;
struct SessionHandle *data = conn->data;
struct FTP *ftp;
char *slash_pos; /* position of the first '/' char in curpos */
char *cur_pos=conn->path; /* current position in ppath. point at the begin
of next path component */
/* the ftp struct is already inited in ftp_connect() */
ftp = conn->proto.ftp;
ftp->ctl_valid = FALSE;
conn->size = -1; /* make sure this is unknown at this point */
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
Curl_pgrsSetUploadSize(data, 0);
Curl_pgrsSetDownloadSize(data, 0);
ftp->dirdepth = 0;
ftp->diralloc = 5; /* default dir depth to allocate */
ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0]));
if(!ftp->dirs)
return CURLE_OUT_OF_MEMORY;
ftp->dirs[0] = NULL; /* to start with */
/* parse the URL path into separate path components */
while((slash_pos=strchr(cur_pos, '/'))) {
/* 1 or 0 to indicate absolute directory */
bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 0);
/* seek out the next path component */
if (slash_pos-cur_pos) {
/* we skip empty path components, like "x//y" since the FTP command CWD
requires a parameter and a non-existant parameter a) doesn't work on
many servers and b) has no effect on the others. */
ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir,
slash_pos - cur_pos +
absolute_dir);
if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
failf(data, "no memory");
freedirs(ftp);
return CURLE_OUT_OF_MEMORY;
}
}
else {
cur_pos = slash_pos + 1; /* jump to the rest of the string */
continue;
}
if(!retcode) {
cur_pos = slash_pos + 1; /* jump to the rest of the string */
if(++ftp->dirdepth >= ftp->diralloc) {
/* enlarge array */
char *bigger;
ftp->diralloc *= 2; /* double the size each time */
bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0]));
if(!bigger) {
freedirs(ftp);
return CURLE_OUT_OF_MEMORY;
}
ftp->dirs = (char **)bigger;
}
}
}
ftp->file = cur_pos; /* the rest is the file name */
if(*ftp->file) {
ftp->file = curl_unescape(ftp->file, 0);
if(NULL == ftp->file) {
freedirs(ftp);
failf(data, "no memory");
return CURLE_OUT_OF_MEMORY;
}
}
else
ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
pointer */
retcode = ftp_perform(conn, &connected);
if(CURLE_OK == retcode) {
if(connected)
retcode = Curl_ftp_nextconnect(conn);
if(retcode && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
/* Failure detected, close the second socket if it was created already */
sclose(conn->sock[SECONDARYSOCKET]);
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
}
if(ftp->no_transfer)
/* no data to transfer */
retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
else if(!connected)
/* since we didn't connect now, we want do_more to get called */
conn->bits.do_more = TRUE;
}
else
freedirs(ftp);
ftp->ctl_valid = TRUE; /* seems good */
return retcode;
}
/***********************************************************************
*
* ftp_3rdparty()
*
* The input argument is already checked for validity.
* Performs a 3rd party transfer between two remote hosts.
*/
static CURLcode ftp_3rdparty(struct connectdata *conn)
{
CURLcode retcode = CURLE_OK;
conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE;
conn->size = conn->sec_conn->size = -1;
retcode = ftp_3rdparty_pretransfer(conn);
if (!retcode)
retcode = ftp_3rdparty_transfer(conn);
return retcode;
}
#endif /* CURL_DISABLE_FTP */

View File

@ -740,7 +740,8 @@ CURLcode add_buffer_send(send_buffer *in,
if(conn->data->set.verbose)
/* this data _may_ contain binary stuff */
Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, amount);
Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, amount,
conn->host.dispname);
*bytes_written += amount;
@ -1044,7 +1045,8 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
/* output debug output if that is requested */
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline);
Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline,
conn->host.dispname);
/* send the header to the callback */
writetype = CLIENTWRITE_HEADER;

View File

@ -1,8 +1,8 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
@ -10,7 +10,7 @@
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
@ -123,7 +123,7 @@ void curl_slist_free_all(struct curl_slist *list)
item = list;
do {
next = item->next;
if (item->data) {
free(item->data);
}
@ -142,7 +142,7 @@ void Curl_infof(struct SessionHandle *data, const char *fmt, ...)
va_start(ap, fmt);
vsnprintf(print_buffer, 1024, fmt, ap);
va_end(ap);
Curl_debug(data, CURLINFO_TEXT, print_buffer, strlen(print_buffer));
Curl_debug(data, CURLINFO_TEXT, print_buffer, strlen(print_buffer), NULL);
}
}
@ -166,7 +166,7 @@ void Curl_failf(struct SessionHandle *data, const char *fmt, ...)
data->set.errorbuffer[len] = '\n';
data->set.errorbuffer[++len] = '\0';
}
Curl_debug(data, CURLINFO_TEXT, data->set.errorbuffer, len);
Curl_debug(data, CURLINFO_TEXT, data->set.errorbuffer, len, NULL);
if(doneit)
/* cut off the newline again */
data->set.errorbuffer[--len]=0;
@ -204,7 +204,7 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn,
break;
if(data->set.verbose)
Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written);
Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written, conn->host.dispname);
if((size_t)bytes_written != write_len) {
/* if not all was written at once, we must advance the pointer, decrease
@ -250,7 +250,7 @@ CURLcode Curl_write(struct connectdata *conn,
if(rc < 0) {
err = SSL_get_error(conn->ssl[num].handle, rc);
switch(err) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
@ -355,7 +355,7 @@ CURLcode Curl_client_write(struct SessionHandle *data,
return CURLE_WRITE_ERROR;
}
}
return CURLE_OK;
}
@ -440,8 +440,8 @@ int Curl_read(struct connectdata *conn, /* connection data */
}
/* return 0 on success */
int Curl_debug(struct SessionHandle *data, curl_infotype type,
char *ptr, size_t size)
static int showit(struct SessionHandle *data, curl_infotype type,
char *ptr, size_t size)
{
static const char * const s_infotype[CURLINFO_END] = {
"* ", "< ", "> ", "{ ", "} " };
@ -462,3 +462,18 @@ int Curl_debug(struct SessionHandle *data, curl_infotype type,
}
return 0;
}
int Curl_debug(struct SessionHandle *data, curl_infotype type,
char *ptr, size_t size, char *host)
{
int rc;
if(data->set.printhost && host) {
char buffer[160];
snprintf(buffer, sizeof(buffer), "[Chunk to/from %s]", host);
rc = showit(data, CURLINFO_TEXT, buffer, strlen(buffer));
if(rc)
return rc;
}
rc = showit(data, type, ptr, size);
return rc;
}

View File

@ -50,7 +50,7 @@ CURLcode Curl_write(struct connectdata *conn,
/* the function used to output verbose information */
int Curl_debug(struct SessionHandle *handle, curl_infotype type,
char *data, size_t size);
char *data, size_t size, char *host);
#endif

View File

@ -875,7 +875,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(data->set.verbose)
Curl_debug(data, CURLINFO_HEADER_IN,
k->p, k->hbuflen);
k->p, k->hbuflen, conn->host.dispname);
result = Curl_client_write(data, writetype, k->p, k->hbuflen);
if(result)
@ -962,12 +962,12 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(data->set.verbose) {
if(k->badheader) {
Curl_debug(data, CURLINFO_DATA_IN, data->state.headerbuff,
k->hbuflen);
k->hbuflen, conn->host.dispname);
if(k->badheader == HEADER_PARTHEADER)
Curl_debug(data, CURLINFO_DATA_IN, k->str, nread);
Curl_debug(data, CURLINFO_DATA_IN, k->str, nread, conn->host.dispname);
}
else
Curl_debug(data, CURLINFO_DATA_IN, k->str, nread);
Curl_debug(data, CURLINFO_DATA_IN, k->str, nread, conn->host.dispname);
}
if(conn->bits.chunk) {
@ -1187,7 +1187,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(data->set.verbose)
/* show the data before we change the pointer upload_fromhere */
Curl_debug(data, CURLINFO_DATA_OUT, conn->upload_fromhere,
bytes_written);
bytes_written, conn->host.dispname);
if(conn->upload_present != bytes_written) {
/* we only wrote a part of the buffer (if anything), deal with it! */
@ -1919,6 +1919,50 @@ CURLcode Curl_follow(struct SessionHandle *data,
return CURLE_OK;
}
static CURLcode
Curl_connect_host(struct SessionHandle *data,
struct connectdata **conn)
{
CURLcode res = CURLE_OK;
int urlchanged = FALSE;
do {
bool async;
Curl_pgrsTime(data, TIMER_STARTSINGLE);
data->change.url_changed = FALSE;
res = Curl_connect(data, conn, &async);
if((CURLE_OK == res) && async) {
/* Now, if async is TRUE here, we need to wait for the name
to resolve */
res = Curl_wait_for_resolv(*conn, NULL);
if(CURLE_OK == res)
/* Resolved, continue with the connection */
res = Curl_async_resolved(*conn);
}
if(res)
break;
/* If a callback (or something) has altered the URL we should use within
the Curl_connect(), we detect it here and act as if we are redirected
to the new URL */
urlchanged = data->change.url_changed;
if ((CURLE_OK == res) && urlchanged) {
res = Curl_done(conn, res);
if(CURLE_OK == res) {
char *gotourl = strdup(data->change.url);
res = Curl_follow(data, gotourl);
if(res)
free(gotourl);
}
}
} while (urlchanged && res == CURLE_OK);
return res;
}
/*
* Curl_perform() is the internal high-level function that gets called by the
* external curl_easy_perform() function. It inits, performs and cleans up a
@ -1945,43 +1989,21 @@ CURLcode Curl_perform(struct SessionHandle *data)
*/
do {
int urlchanged = FALSE;
do {
bool async;
Curl_pgrsTime(data, TIMER_STARTSINGLE);
data->change.url_changed = FALSE;
res = Curl_connect(data, &conn, &async);
if((CURLE_OK == res) && async) {
/* Now, if async is TRUE here, we need to wait for the name
to resolve */
res = Curl_wait_for_resolv(conn, NULL);
if(CURLE_OK == res)
/* Resolved, continue with the connection */
res = Curl_async_resolved(conn);
}
if(res)
break;
/* If a callback (or something) has altered the URL we should use within
the Curl_connect(), we detect it here and act as if we are redirected
to the new URL */
urlchanged = data->change.url_changed;
if ((CURLE_OK == res) && urlchanged) {
res = Curl_done(&conn, res);
if(CURLE_OK == res) {
char *gotourl = strdup(data->change.url);
res = Curl_follow(data, gotourl);
if(res)
free(gotourl);
}
}
} while (urlchanged && res == CURLE_OK);
res = Curl_connect_host(data, &conn); /* primary connection */
if(res == CURLE_OK) {
if (data->set.source_host) /* 3rd party transfer */
res = Curl_pretransfersec(conn);
else
conn->sec_conn = NULL;
}
if(res == CURLE_OK) {
res = Curl_do(&conn);
if(res == CURLE_OK) {
/* for non 3rd party transfer only */
if(res == CURLE_OK && !data->set.source_host) {
res = Transfer(conn); /* now fetch that URL please */
if(res == CURLE_OK) {
@ -2099,3 +2121,35 @@ Curl_Transfer(struct connectdata *c_conn, /* connection data */
return CURLE_OK;
}
/*
* Curl_pretransfersec() prepares the secondary connection (used for 3rd party
* FTP transfers).
*/
CURLcode Curl_pretransfersec(struct connectdata *conn)
{
CURLcode status = CURLE_OK;
struct SessionHandle *data = conn->data;
struct connectdata *sec_conn = NULL; /* secondary connection */
/* update data with source host options */
char *url = aprintf( "%s://%s/", conn->protostr, data->set.source_host);
if(!url)
return CURLE_OUT_OF_MEMORY;
if(data->change.url_alloc)
free(data->change.url);
data->change.url_alloc = TRUE;
data->change.url = url;
data->set.ftpport = data->set.source_port;
data->set.userpwd = data->set.source_userpwd;
/* secondary connection */
status = Curl_connect_host(data, &sec_conn);
sec_conn->data = data;
conn->sec_conn = sec_conn;
return status;
}

View File

@ -24,6 +24,7 @@
***************************************************************************/
CURLcode Curl_perform(struct SessionHandle *data);
CURLcode Curl_pretransfer(struct SessionHandle *data);
CURLcode Curl_pretransfersec(struct connectdata *conn);
CURLcode Curl_posttransfer(struct SessionHandle *data);
CURLcode Curl_follow(struct SessionHandle *data, char *newurl);
CURLcode Curl_readwrite(struct connectdata *conn, bool *done);

View File

@ -1337,6 +1337,57 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
data->set.tcp_nodelay = (bool)va_arg(param, long);
break;
/*********** 3rd party transfer options ***********/
case CURLOPT_SOURCE_HOST:
/*
* Use SOURCE HOST
*/
data->set.source_host = va_arg(param, char *);
data->set.printhost = (data->set.source_host != NULL);
break;
case CURLOPT_SOURCE_PORT:
/*
* Use SOURCE PORT
*/
data->set.source_port = va_arg(param, char *);
break;
case CURLOPT_SOURCE_USERPWD:
/*
* Use SOURCE USER[:PASSWORD]
*/
data->set.source_userpwd = va_arg(param, char *);
break;
case CURLOPT_SOURCE_PATH:
/*
* Use SOURCE PATH
*/
data->set.source_path = va_arg(param, char *);
break;
case CURLOPT_PASV_HOST:
/*
* Indicates whether source or target host is passive
*/
data->set.pasvHost = va_arg(param, long)?CURL_SOURCE_PASV:CURL_TARGET_PASV;
break;
case CURLOPT_SOURCE_PREQUOTE:
/*
* List of RAW FTP commands to use before a transfer on the source host
*/
data->set.source_prequote = va_arg(param, struct curl_slist *);
break;
case CURLOPT_SOURCE_POSTQUOTE:
/*
* List of RAW FTP commands to use after a transfer on the source host
*/
data->set.source_postquote = va_arg(param, struct curl_slist *);
break;
default:
/* unknown tag and its companion, just ignore: */
return CURLE_FAILED_INIT; /* correct this */

View File

@ -185,6 +185,12 @@ typedef enum {
NTLMSTATE_LAST
} curlntlm;
/* for 3rd party transfers to decide which side that issues PASV */
typedef enum {
CURL_TARGET_PASV,
CURL_SOURCE_PASV
} curl_pasv_side;
/* Struct used for NTLM challenge-response authentication */
struct ntlmdata {
curlntlm state;
@ -601,6 +607,8 @@ struct connectdata {
/* data used for the asynch name resolve callback */
struct Curl_async async;
#endif
struct connectdata *sec_conn; /* secondary connection for 3rd party
transfer */
};
/* The end of connectdata. */
@ -845,7 +853,11 @@ struct UserDefined {
bool crlf; /* convert crlf on ftp upload(?) */
struct curl_slist *quote; /* after connection is established */
struct curl_slist *postquote; /* after the transfer */
struct curl_slist *prequote; /* before the transfer, after type (Wesley Laxton)*/
struct curl_slist *prequote; /* before the transfer, after type */
struct curl_slist *source_prequote; /* in 3rd party transfer mode - before
the transfer on source host */
struct curl_slist *source_postquote; /* in 3rd party transfer mode - after
the transfer on source host */
struct curl_slist *telnet_options; /* linked list of telnet options */
curl_TimeCond timecondition; /* kind of time/date comparison */
time_t timevalue; /* what time to compare with */
@ -874,10 +886,17 @@ struct UserDefined {
curl_off_t max_filesize; /* Maximum file size to download */
char *source_host; /* for 3rd party transfer */
char *source_port; /* for 3rd party transfer */
char *source_userpwd; /* for 3rd party transfer */
char *source_path; /* for 3rd party transfer */
curl_pasv_side pasvHost; /* for 3rd party transfer indicates passive host */
/* Here follows boolean settings that define how to behave during
this session. They are STATIC, set by libcurl users or at least initially
and they don't change during operations. */
bool printhost; /* printing host name in debug info */
bool get_filetime;
bool tunnel_thru_httpproxy;
bool ftp_append;