Newer
Older
Daniel Stenberg
committed
Curl_pgrsSetDownloadSize(data, size);
}
/* We can resume if we can seek to the resume position */
if(data->state.resume_from) {
if(data->state.resume_from < 0) {
Daniel Stenberg
committed
/* We're supposed to download the last abs(from) bytes */
if((curl_off_t)attrs.filesize < -data->state.resume_from) {
failf(data, "Offset (%"
FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")",
data->state.resume_from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
/* download from where? */
data->state.resume_from += attrs.filesize;
Daniel Stenberg
committed
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
}
else {
if((curl_off_t)attrs.filesize < data->state.resume_from) {
failf(data, "Offset (%" FORMAT_OFF_T
") was beyond file size (%" FORMAT_OFF_T ")",
data->state.resume_from, attrs.filesize);
return CURLE_BAD_DOWNLOAD_RESUME;
}
}
/* Does a completed file need to be seeked and started or closed ? */
/* Now store the number of bytes we are expected to download */
data->req.size = attrs.filesize - data->state.resume_from;
data->req.maxdownload = attrs.filesize - data->state.resume_from;
Curl_pgrsSetDownloadSize(data,
attrs.filesize - data->state.resume_from);
SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
}
}
/* Setup the actual download */
if(data->req.size == 0) {
/* no data to transfer */
Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
Daniel Stenberg
committed
infof(data, "File already completely downloaded\n");
state(conn, SSH_STOP);
break;
}
Daniel Stenberg
committed
else {
Curl_setup_transfer(conn, FIRSTSOCKET, data->req.size,
FALSE, NULL, -1, NULL);
Kamil Dudka
committed
Daniel Stenberg
committed
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->writesockfd = conn->sockfd;
Kamil Dudka
committed
/* we want to use the _receiving_ function even when the socket turns
out writableable as the underlying libssh2 recv function will deal
with both accordingly */
Daniel Stenberg
committed
conn->cselect_bits = CURL_CSELECT_IN;
}
if(result) {
state(conn, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
state(conn, SSH_STOP);
}
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_CLOSE:
if(sshc->sftp_handle) {
rc = libssh2_sftp_close(sshc->sftp_handle);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to close libssh2 file\n");
}
sshc->sftp_handle = NULL;
}
Curl_safefree(sftp_scp->path);
James Housley
committed
Daniel Stenberg
committed
DEBUGF(infof(data, "SFTP DONE done\n"));
/* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
After nextstate is executed,the control should come back to
SSH_SFTP_CLOSE to pass the correct result back */
if(sshc->nextstate != SSH_NO_STATE) {
state(conn, sshc->nextstate);
sshc->nextstate = SSH_SFTP_CLOSE;
}
else {
state(conn, SSH_STOP);
result = sshc->actualcode;
}
Daniel Stenberg
committed
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SFTP_SHUTDOWN:
/* during times we get here due to a broken transfer and then the
sftp_handle might not have been taken down so make sure that is done
before we proceed */
Daniel Stenberg
committed
Daniel Stenberg
committed
if(sshc->sftp_handle) {
rc = libssh2_sftp_close(sshc->sftp_handle);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to close libssh2 file\n");
}
sshc->sftp_handle = NULL;
James Housley
committed
}
Daniel Stenberg
committed
if(sshc->sftp_session) {
rc = libssh2_sftp_shutdown(sshc->sftp_session);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to stop libssh2 sftp subsystem\n");
}
sshc->sftp_session = NULL;
}
James Housley
committed
Daniel Stenberg
committed
Curl_safefree(sshc->homedir);
conn->data->state.most_recent_ftp_entrypath = NULL;
James Housley
committed
Daniel Stenberg
committed
state(conn, SSH_SESSION_DISCONNECT);
James Housley
committed
break;
Daniel Stenberg
committed
case SSH_SCP_TRANS_INIT:
result = ssh_getworkingpath(conn, sshc->homedir, &sftp_scp->path);
if(result) {
sshc->actualcode = result;
state(conn, SSH_STOP);
break;
James Housley
committed
}
Daniel Stenberg
committed
if(data->set.upload) {
if(data->set.infilesize < 0) {
failf(data, "SCP requires a known file size for upload");
sshc->actualcode = CURLE_UPLOAD_FAILED;
state(conn, SSH_SCP_CHANNEL_FREE);
break;
}
state(conn, SSH_SCP_UPLOAD_INIT);
James Housley
committed
}
else {
Daniel Stenberg
committed
state(conn, SSH_SCP_DOWNLOAD_INIT);
}
Daniel Stenberg
committed
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SCP_UPLOAD_INIT:
/*
* libssh2 requires that the destination path is a full path that
* includes the destination file and name OR ends in a "/" . If this is
* not done the destination file will be named the same name as the last
* directory in the path.
*/
sshc->ssh_channel =
SCP_SEND(sshc->ssh_session, sftp_scp->path, data->set.new_file_perms,
data->set.infilesize);
Daniel Stenberg
committed
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
int ssh_err;
char *err_msg;
ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
&err_msg, NULL, 0));
Daniel Stenberg
committed
failf(conn->data, "%s", err_msg);
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
break;
}
}
James Housley
committed
Daniel Stenberg
committed
/* upload data */
Curl_setup_transfer(conn, -1, data->req.size, FALSE, NULL,
FIRSTSOCKET, NULL);
Kamil Dudka
committed
Daniel Stenberg
committed
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->sockfd = conn->writesockfd;
Daniel Stenberg
committed
if(result) {
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = result;
James Housley
committed
}
else {
/* we want to use the _sending_ function even when the socket turns
out readable as the underlying libssh2 scp send function will deal
with both accordingly */
conn->cselect_bits = CURL_CSELECT_OUT;
Daniel Stenberg
committed
state(conn, SSH_STOP);
James Housley
committed
}
Daniel Stenberg
committed
break;
James Housley
committed
Daniel Stenberg
committed
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
case SSH_SCP_DOWNLOAD_INIT:
{
/*
* We must check the remote file; if it is a directory no values will
* be set in sb
*/
struct stat sb;
curl_off_t bytecount;
/* clear the struct scp recv will fill in */
memset(&sb, 0, sizeof(struct stat));
/* get a fresh new channel from the ssh layer */
sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
sftp_scp->path, &sb);
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
int ssh_err;
char *err_msg;
ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
&err_msg, NULL, 0));
Daniel Stenberg
committed
failf(conn->data, "%s", err_msg);
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
break;
}
}
Kamil Dudka
committed
Daniel Stenberg
committed
/* download data */
bytecount = (curl_off_t)sb.st_size;
data->req.maxdownload = (curl_off_t)sb.st_size;
Curl_setup_transfer(conn, FIRSTSOCKET, bytecount, FALSE, NULL, -1, NULL);
Kamil Dudka
committed
Daniel Stenberg
committed
/* not set by Curl_setup_transfer to preserve keepon bits */
conn->writesockfd = conn->sockfd;
James Housley
committed
/* we want to use the _receiving_ function even when the socket turns
out writableable as the underlying libssh2 recv function will deal
with both accordingly */
Daniel Stenberg
committed
conn->cselect_bits = CURL_CSELECT_IN;
James Housley
committed
Daniel Stenberg
committed
if(result) {
state(conn, SSH_SCP_CHANNEL_FREE);
sshc->actualcode = result;
James Housley
committed
}
Daniel Stenberg
committed
else
state(conn, SSH_STOP);
}
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SCP_DONE:
if(data->set.upload)
state(conn, SSH_SCP_SEND_EOF);
else
state(conn, SSH_SCP_CHANNEL_FREE);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SCP_SEND_EOF:
if(sshc->ssh_channel) {
rc = libssh2_channel_send_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
infof(data, "Failed to send libssh2 channel EOF\n");
}
James Housley
committed
}
Daniel Stenberg
committed
state(conn, SSH_SCP_WAIT_EOF);
break;
case SSH_SCP_WAIT_EOF:
if(sshc->ssh_channel) {
rc = libssh2_channel_wait_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
infof(data, "Failed to get channel EOF: %d\n", rc);
}
James Housley
committed
}
Daniel Stenberg
committed
state(conn, SSH_SCP_WAIT_CLOSE);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SCP_WAIT_CLOSE:
if(sshc->ssh_channel) {
rc = libssh2_channel_wait_closed(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc) {
infof(data, "Channel failed to close: %d\n", rc);
}
James Housley
committed
}
Daniel Stenberg
committed
state(conn, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_CHANNEL_FREE:
if(sshc->ssh_channel) {
rc = libssh2_channel_free(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to free libssh2 scp subsystem\n");
}
sshc->ssh_channel = NULL;
James Housley
committed
}
Daniel Stenberg
committed
DEBUGF(infof(data, "SCP DONE phase complete\n"));
#if 0 /* PREV */
Daniel Stenberg
committed
state(conn, SSH_SESSION_DISCONNECT);
#endif
Daniel Stenberg
committed
state(conn, SSH_STOP);
result = sshc->actualcode;
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SESSION_DISCONNECT:
/* during weird times when we've been prematurely aborted, the channel
is still alive when we reach this state and we MUST kill the channel
properly first */
if(sshc->ssh_channel) {
rc = libssh2_channel_free(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to free libssh2 scp subsystem\n");
}
sshc->ssh_channel = NULL;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(sshc->ssh_session) {
rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to disconnect libssh2 session\n");
}
}
James Housley
committed
Daniel Stenberg
committed
Curl_safefree(sshc->homedir);
conn->data->state.most_recent_ftp_entrypath = NULL;
James Housley
committed
Daniel Stenberg
committed
state(conn, SSH_SESSION_FREE);
break;
James Housley
committed
Daniel Stenberg
committed
case SSH_SESSION_FREE:
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
Daniel Stenberg
committed
if(sshc->kh) {
libssh2_knownhost_free(sshc->kh);
sshc->kh = NULL;
}
#endif
Daniel Stenberg
committed
if(sshc->ssh_session) {
rc = libssh2_session_free(sshc->ssh_session);
if(rc == LIBSSH2_ERROR_EAGAIN) {
break;
}
else if(rc < 0) {
infof(data, "Failed to free libssh2 session\n");
}
sshc->ssh_session = NULL;
}
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
/* worst-case scenario cleanup */
DEBUGASSERT(sshc->ssh_session == NULL);
DEBUGASSERT(sshc->ssh_channel == NULL);
DEBUGASSERT(sshc->sftp_session == NULL);
DEBUGASSERT(sshc->sftp_handle == NULL);
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
DEBUGASSERT(sshc->kh == NULL);
#endif
Curl_safefree(sshc->rsa_pub);
Curl_safefree(sshc->rsa);
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
Curl_safefree(sshc->homedir);
Curl_safefree(sshc->readdir_filename);
Curl_safefree(sshc->readdir_longentry);
Curl_safefree(sshc->readdir_line);
Curl_safefree(sshc->readdir_linkPath);
/* the code we are about to return */
result = sshc->actualcode;
memset(sshc, 0, sizeof(struct ssh_conn));
Daniel Stenberg
committed
conn->bits.close = TRUE;
sshc->state = SSH_SESSION_FREE; /* current */
Daniel Stenberg
committed
sshc->nextstate = SSH_NO_STATE;
state(conn, SSH_STOP);
break;
case SSH_QUIT:
/* fallthrough, just stop! */
default:
/* internal error */
sshc->nextstate = SSH_NO_STATE;
state(conn, SSH_STOP);
break;
}
Daniel Stenberg
committed
} while(!rc && (sshc->state != SSH_STOP));
if(rc == LIBSSH2_ERROR_EAGAIN) {
/* we would block, we need to wait for the socket to be ready (in the
right direction too)! */
*block = TRUE;
}
return result;
}
Daniel Stenberg
committed
/* called by the multi interface to figure out what socket(s) to wait for and
for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
static int ssh_perform_getsock(const struct connectdata *conn,
curl_socket_t *sock, /* points to numsocks
number of sockets */
int numsocks)
{
Guenter Knauf
committed
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
Daniel Stenberg
committed
int bitmap = GETSOCK_BLANK;
(void)numsocks;
sock[0] = conn->sock[FIRSTSOCKET];
Kamil Dudka
committed
if(conn->waitfor & KEEP_RECV)
Daniel Stenberg
committed
bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
Kamil Dudka
committed
if(conn->waitfor & KEEP_SEND)
Daniel Stenberg
committed
bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
return bitmap;
#else
/* if we don't know the direction we can use the generic *_getsock()
function even for the protocol_connect and doing states */
return Curl_single_getsock(conn, sock, numsocks);
#endif
}
/* Generic function called by the multi interface to figure out what socket(s)
to wait for and for what actions during the DOING and PROTOCONNECT states*/
static int ssh_getsock(struct connectdata *conn,
curl_socket_t *sock, /* points to numsocks number
of sockets */
int numsocks)
{
Guenter Knauf
committed
#ifndef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
(void)conn;
(void)sock;
(void)numsocks;
Daniel Stenberg
committed
/* if we don't know any direction we can just play along as we used to and
not provide any sensible info */
return GETSOCK_BLANK;
#else
/* if we know the direction we can use the generic *_getsock() function even
for the protocol_connect and doing states */
return ssh_perform_getsock(conn, sock, numsocks);
#endif
}
Guenter Knauf
committed
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
Daniel Stenberg
committed
/*
* When one of the libssh2 functions has returned LIBSSH2_ERROR_EAGAIN this
* function is used to figure out in what direction and stores this info so
* that the multi interface can take advantage of it. Make sure to call this
* function in all cases so that when it _doesn't_ return EAGAIN we can
* restore the default wait bits.
*/
static void ssh_block2waitfor(struct connectdata *conn, bool block)
{
struct ssh_conn *sshc = &conn->proto.sshc;
int dir;
Kamil Dudka
committed
if(!block)
conn->waitfor = 0;
else if((dir = libssh2_session_block_directions(sshc->ssh_session))) {
Daniel Stenberg
committed
/* translate the libssh2 define bits into our own bit defines */
Kamil Dudka
committed
conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) |
((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0);
Daniel Stenberg
committed
}
else
/* It didn't block or libssh2 didn't reveal in which direction, put back
the original set */
Kamil Dudka
committed
conn->waitfor = sshc->orig_waitfor;
Daniel Stenberg
committed
}
#else
/* no libssh2 directional support so we simply don't know */
#define ssh_block2waitfor(x,y) Curl_nop_stmt
Daniel Stenberg
committed
#endif
/* called repeatedly until done from multi.c */
static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done)
{
struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
Daniel Stenberg
committed
bool block; /* we store the status and use that to provide a ssh_getsock()
implementation */
Daniel Stenberg
committed
result = ssh_statemach_act(conn, &block);
*done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
Daniel Stenberg
committed
ssh_block2waitfor(conn, block);
return result;
}
static CURLcode ssh_easy_statemach(struct connectdata *conn,
bool duringconnect)
{
struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
while((sshc->state != SSH_STOP) && !result) {
bool block;
result = ssh_statemach_act(conn, &block);
if(Curl_pgrsUpdate(conn))
return CURLE_ABORTED_BY_CALLBACK;
else {
struct timeval now = Curl_tvnow();
result = Curl_speedcheck(data, now);
if(result)
left = Curl_timeleft(data, NULL, duringconnect);
if(left < 0) {
failf(data, "Operation timed out\n");
return CURLE_OPERATION_TIMEDOUT;
}
Guenter Knauf
committed
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
if((CURLE_OK == result) && block) {
int dir = libssh2_session_block_directions(sshc->ssh_session);
curl_socket_t sock = conn->sock[FIRSTSOCKET];
curl_socket_t fd_read = CURL_SOCKET_BAD;
curl_socket_t fd_write = CURL_SOCKET_BAD;
if(LIBSSH2_SESSION_BLOCK_INBOUND & dir)
fd_read = sock;
if(LIBSSH2_SESSION_BLOCK_OUTBOUND & dir)
fd_write = sock;
/* wait for the socket to become ready */
Curl_socket_ready(fd_read, fd_write,
left>1000?1000:left); /* ignore result */
}
#endif
}
James Housley
committed
return result;
}
/*
* SSH setup and connection
*/
static CURLcode ssh_init(struct connectdata *conn)
{
struct SessionHandle *data = conn->data;
struct SSHPROTO *ssh;
struct ssh_conn *sshc = &conn->proto.sshc;
Daniel Stenberg
committed
sshc->actualcode = CURLE_OK; /* reset error code */
sshc->secondCreateDirs =0; /* reset the create dir attempt state
variable */
Daniel Stenberg
committed
Daniel Stenberg
committed
if(data->state.proto.ssh)
return CURLE_OK;
ssh = calloc(1, sizeof(struct SSHPROTO));
if(!ssh)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
data->state.proto.ssh = ssh;
return CURLE_OK;
}
static Curl_recv scp_recv, sftp_recv;
static Curl_send scp_send, sftp_send;
* Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
* do protocol-specific actions at connect-time.
*/
static CURLcode ssh_connect(struct connectdata *conn, bool *done)
CURLcode result;
struct SessionHandle *data = conn->data;
/* We default to persistent connections. We set this already in this connect
function to make the re-use checks properly be able to check this bit. */
conn->bits.close = FALSE;
/* If there already is a protocol-specific struct allocated for this
sessionhandle, deal with it */
Curl_reset_reqproto(conn);
Daniel Stenberg
committed
result = ssh_init(conn);
if(result)
if(conn->handler->protocol & CURLPROTO_SCP) {
conn->recv[FIRSTSOCKET] = scp_recv;
conn->send[FIRSTSOCKET] = scp_send;
conn->recv[FIRSTSOCKET] = sftp_recv;
conn->send[FIRSTSOCKET] = sftp_send;
ssh = &conn->proto.sshc;
#ifdef CURL_LIBSSH2_DEBUG
if(conn->user) {
infof(data, "User: %s\n", conn->user);
if(conn->passwd) {
infof(data, "Password: %s\n", conn->passwd);
}
sock = conn->sock[FIRSTSOCKET];
ssh->ssh_session = libssh2_session_init_ex(my_libssh2_malloc,
my_libssh2_free,
my_libssh2_realloc, conn);
if(ssh->ssh_session == NULL) {
failf(data, "Failure initialising ssh session");
return CURLE_FAILED_INIT;
}
Daniel Stenberg
committed
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
int rc;
ssh->kh = libssh2_knownhost_init(ssh->ssh_session);
if(!ssh->kh) {
/* eeek. TODO: free the ssh_session! */
return CURLE_FAILED_INIT;
}
/* read all known hosts from there */
rc = libssh2_knownhost_readfile(ssh->kh,
data->set.str[STRING_SSH_KNOWNHOSTS],
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
Daniel Stenberg
committed
infof(data, "Failed to read known hosts from %s\n",
data->set.str[STRING_SSH_KNOWNHOSTS]);
}
#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
#ifdef CURL_LIBSSH2_DEBUG
Daniel Stenberg
committed
libssh2_trace(ssh->ssh_session, ~0);
#endif /* CURL_LIBSSH2_DEBUG */
if(data->state.used_interface == Curl_if_multi)
result = ssh_multi_statemach(conn, done);
result = ssh_easy_statemach(conn, TRUE);
if(!result)
*done = TRUE;
}
return result;
James Housley
committed
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
/*
***********************************************************************
*
* scp_perform()
*
* This is the actual DO function for SCP. Get a file according to
* the options previously setup.
*/
static
CURLcode scp_perform(struct connectdata *conn,
bool *connected,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
DEBUGF(infof(conn->data, "DO phase starts\n"));
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
state(conn, SSH_SCP_TRANS_INIT);
/* run the state-machine */
if(conn->data->state.used_interface == Curl_if_multi) {
result = ssh_multi_statemach(conn, dophase_done);
}
else {
result = ssh_easy_statemach(conn, FALSE);
James Housley
committed
*dophase_done = TRUE; /* with the easy interface we are done here */
}
*connected = conn->bits.tcpconnect[FIRSTSOCKET];
James Housley
committed
if(*dophase_done) {
James Housley
committed
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
/* called from multi.c while DOing */
static CURLcode scp_doing(struct connectdata *conn,
Patrick Monnerat
committed
bool *dophase_done)
James Housley
committed
{
CURLcode result;
result = ssh_multi_statemach(conn, dophase_done);
James Housley
committed
if(*dophase_done) {
James Housley
committed
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
/*
* The DO function is generic for both protocols. There was previously two
* separate ones but this way means less duplicated code.
*/
James Housley
committed
static CURLcode ssh_do(struct connectdata *conn, bool *done)
James Housley
committed
CURLcode res;
bool connected = 0;
struct SessionHandle *data = conn->data;
*done = FALSE; /* default to false */
Daniel Stenberg
committed
/*
Since connections can be re-used between SessionHandles, this might be a
connection already existing but on a fresh SessionHandle struct so we must
make sure we have a good 'struct SSHPROTO' to play with. For new
connections, the struct SSHPROTO is allocated and setup in the
ssh_connect() function.
*/
Curl_reset_reqproto(conn);
res = ssh_init(conn);
if(res)
return res;
Daniel Stenberg
committed
data->req.size = -1; /* make sure this is unknown at this point */
James Housley
committed
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
Curl_pgrsSetUploadSize(data, 0);
Curl_pgrsSetDownloadSize(data, 0);
if(conn->handler->protocol & CURLPROTO_SCP)
res = scp_perform(conn, &connected, done);
else
res = sftp_perform(conn, &connected, done);
James Housley
committed
return res;
/* BLOCKING, but the function is using the state machine so the only reason
this is still blocking is that the multi interface code has no support for
disconnecting operations that takes a while */
static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection)
{
CURLcode result = CURLE_OK;
struct ssh_conn *ssh = &conn->proto.sshc;
(void) dead_connection;
Daniel Stenberg
committed
Curl_safefree(conn->data->state.proto.ssh);
if(ssh->ssh_session) {
Daniel Stenberg
committed
/* only if there's a session still around to use! */
state(conn, SSH_SESSION_DISCONNECT);
result = ssh_easy_statemach(conn, FALSE);
Daniel Stenberg
committed
}
return result;
}
Daniel Stenberg
committed
/* generic done function for both SCP and SFTP called from their specific
done functions */
static CURLcode ssh_done(struct connectdata *conn, CURLcode status)
James Housley
committed
CURLcode result = CURLE_OK;
struct SSHPROTO *sftp_scp = conn->data->state.proto.ssh;
James Housley
committed
if(status == CURLE_OK) {
/* run the state-machine
TODO: when the multi interface is used, this _really_ should be using
the ssh_multi_statemach function but we have no general support for
non-blocking DONE operations, not in the multi state machine and with
Curl_done() invokes on several places in the code!
*/
result = ssh_easy_statemach(conn, FALSE);
}
else
James Housley
committed
result = status;
Daniel Stenberg
committed
Curl_safefree(sftp_scp->path);
Curl_pgrsDone(conn);
James Housley
committed
Daniel Stenberg
committed
conn->data->req.keepon = 0; /* clear all bits */
James Housley
committed
return result;
Daniel Stenberg
committed
}
static CURLcode scp_done(struct connectdata *conn, CURLcode status,
bool premature)
{
(void)premature; /* not used */
if(status == CURLE_OK)
state(conn, SSH_SCP_DONE);
return ssh_done(conn, status);
}
/* return number of received (decrypted) bytes */
static ssize_t scp_send(struct connectdata *conn, int sockindex,
const void *mem, size_t len, CURLcode *err)
{
ssize_t nwrite;
(void)sockindex; /* we only support SCP on the fixed known primary socket */
/* libssh2_channel_write() returns int! */
nwrite = (ssize_t)
libssh2_channel_write(conn->proto.sshc.ssh_channel, mem, len);
Daniel Stenberg
committed
ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
if(nwrite == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
nwrite = 0;
}
return nwrite;
}
/*
* If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
* a regular CURLcode value.
*/
static ssize_t scp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len, CURLcode *err)
{
ssize_t nread;
(void)sockindex; /* we only support SCP on the fixed known primary socket */
/* libssh2_channel_read() returns int */
nread = (ssize_t)
libssh2_channel_read(conn->proto.sshc.ssh_channel, mem, len);
Daniel Stenberg
committed
ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
if(nread == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
nread = -1;
}
Daniel Stenberg
committed
return nread;
}
/*
* =============== SFTP ===============
*/
James Housley
committed
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
/*
***********************************************************************
*
* sftp_perform()
*
* This is the actual DO function for SFTP. Get a file/directory according to
* the options previously setup.
*/
static
CURLcode sftp_perform(struct connectdata *conn,
bool *connected,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
DEBUGF(infof(conn->data, "DO phase starts\n"));
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
state(conn, SSH_SFTP_QUOTE_INIT);
/* run the state-machine */
if(conn->data->state.used_interface == Curl_if_multi) {
result = ssh_multi_statemach(conn, dophase_done);
}
else {
result = ssh_easy_statemach(conn, FALSE);
James Housley
committed
*dophase_done = TRUE; /* with the easy interface we are done here */
}
*connected = conn->bits.tcpconnect[FIRSTSOCKET];
James Housley
committed
if(*dophase_done) {
James Housley
committed
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
/* called from multi.c while DOing */
static CURLcode sftp_doing(struct connectdata *conn,
bool *dophase_done)
James Housley
committed
{
CURLcode result;
result = ssh_multi_statemach(conn, dophase_done);
James Housley
committed
if(*dophase_done) {
James Housley
committed
DEBUGF(infof(conn->data, "DO phase is complete\n"));
}
return result;
}
/* BLOCKING, but the function is using the state machine so the only reason
this is still blocking is that the multi interface code has no support for
disconnecting operations that takes a while */
static CURLcode sftp_disconnect(struct connectdata *conn, bool dead_connection)
CURLcode result = CURLE_OK;
(void) dead_connection;
James Housley
committed
DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n"));
James Housley
committed
Daniel Stenberg
committed
Curl_safefree(conn->data->state.proto.ssh);
Daniel Stenberg
committed
if(conn->proto.sshc.ssh_session) {
/* only if there's a session still around to use! */
state(conn, SSH_SFTP_SHUTDOWN);
result = ssh_easy_statemach(conn, FALSE);
Daniel Stenberg
committed
}
James Housley
committed
DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n"));
James Housley
committed
return result;
James Housley
committed
static CURLcode sftp_done(struct connectdata *conn, CURLcode status,
Patrick Monnerat
committed
bool premature)
James Housley
committed
struct ssh_conn *sshc = &conn->proto.sshc;
if(status == CURLE_OK) {
/* Post quote commands are executed after the SFTP_CLOSE state to avoid
errors that could happen due to open file handles during POSTQUOTE
operation */
if(!status && !premature && conn->data->set.postquote) {
sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
state(conn, SSH_SFTP_CLOSE);
James Housley
committed
}
else
state(conn, SSH_SFTP_CLOSE);
James Housley
committed
}
Daniel Stenberg
committed
return ssh_done(conn, status);
/* return number of sent bytes */
static ssize_t sftp_send(struct connectdata *conn, int sockindex,
const void *mem, size_t len, CURLcode *err)
ssize_t nwrite; /* libssh2_sftp_write() used to return size_t in 0.14
but is changed to ssize_t in 0.15. These days we don't
support libssh2 0.15*/
(void)sockindex;
nwrite = libssh2_sftp_write(conn->proto.sshc.sftp_handle, mem, len);
Daniel Stenberg
committed
ssh_block2waitfor(conn, (nwrite == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
if(nwrite == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
nwrite = 0;
}
return nwrite;