Newer
Older
Daniel Stenberg
committed
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
/* FIXME: here should be explained why we need it to start the
* download */
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;
}
Daniel Stenberg
committed
Curl_safefree(sftp_scp->path);
sftp_scp->path = NULL;
James Housley
committed
Daniel Stenberg
committed
DEBUGF(infof(data, "SFTP DONE done\n"));
#if 0 /* PREV */
Daniel Stenberg
committed
state(conn, SSH_SFTP_SHUTDOWN);
#endif
Daniel Stenberg
committed
state(conn, SSH_STOP);
result = sshc->actualcode;
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);
sshc->homedir = NULL;
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 {
Daniel Stenberg
committed
state(conn, SSH_STOP);
James Housley
committed
}
Daniel Stenberg
committed
break;
James Housley
committed
Daniel Stenberg
committed
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
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
/* FIXME: here should be explained why we need it to start the
* download */
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);
sshc->homedir = NULL;
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;
}
Daniel Stenberg
committed
conn->bits.close = TRUE;
sshc->nextstate = SSH_NO_STATE;
state(conn, SSH_STOP);
result = sshc->actualcode;
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)
#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);
James Housley
committed
*done = (bool)(sshc->state == SSH_STOP);
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,
(int)(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->protocol & PROT_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);
if(rc) {
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 */
state(conn, SSH_S_STARTUP);
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
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
/*
***********************************************************************
*
* 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;
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->protocol & PROT_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);
conn->data->state.proto.ssh = NULL;
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);
sftp_scp->path = NULL;
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
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
/*
***********************************************************************
*
* 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;
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);
conn->data->state.proto.ssh = NULL;
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) {
/* Before we shut down, see if there are any post-quote commands to
send: */
if(!status && !premature && conn->data->set.postquote) {
sshc->nextstate = SSH_SFTP_CLOSE;
James Housley
committed
state(conn, SSH_SFTP_POSTQUOTE_INIT);
}
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;
}
* Return number of received (decrypted) bytes
static ssize_t sftp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len, CURLcode *err)
{
ssize_t nread;
(void)sockindex;
nread = libssh2_sftp_read(conn->proto.sshc.sftp_handle, mem, len);
Daniel Stenberg
committed
ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE);
if(nread == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
nread = -1;
}
return nread;
}
/* The get_pathname() function is being borrowed from OpenSSH sftp.c
version 4.6p1. */
/*
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
get_pathname(const char **cpp, char **path)
{
const char *cp = *cpp, *end;
char quot;
static const char WHITESPACE[] = " \t\r\n";
cp += strspn(cp, WHITESPACE);
if(!*cp) {
*cpp = cp;
*path = NULL;
return CURLE_QUOTE_ERROR;
}
*path = malloc(strlen(cp) + 1);
if(*path == NULL)
return CURLE_OUT_OF_MEMORY;
/* Check for quoted filenames */
if(*cp == '\"' || *cp == '\'') {
quot = *cp++;
/* Search for terminating quote, unescape some chars */
for (i = j = 0; i <= strlen(cp); i++) {
if(cp[i] == quot) { /* Found quote */