Newer
Older
CURLcode result = CURLE_OK;
while((sshc->state != SSH_STOP) && !result)
James Housley
committed
result = ssh_statemach_act(conn);
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(sizeof(struct SSHPROTO), 1);
if(!ssh)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
data->state.proto.ssh = ssh;
return CURLE_OK;
}
/*
* 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)
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(libssh2_malloc, libssh2_free,
libssh2_realloc, conn);
if(ssh->ssh_session == NULL) {
failf(data, "Failure initialising ssh session");
return CURLE_FAILED_INIT;
}
#ifdef CURL_LIBSSH2_DEBUG
libssh2_trace(ssh->ssh_session, LIBSSH2_TRACE_CONN|LIBSSH2_TRACE_TRANS|
LIBSSH2_TRACE_KEX|LIBSSH2_TRACE_AUTH|LIBSSH2_TRACE_SCP|
LIBSSH2_TRACE_SFTP|LIBSSH2_TRACE_ERROR|
LIBSSH2_TRACE_PUBLICKEY);
infof(data, "SSH socket: %d\n", sock);
#endif /* CURL_LIBSSH2_DEBUG */
state(conn, SSH_S_STARTUP);
if(data->state.used_interface == Curl_if_multi)
result = ssh_multi_statemach(conn, done);
else {
result = ssh_easy_statemach(conn);
if(!result)
*done = TRUE;
}
return result;
James Housley
committed
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
/*
***********************************************************************
*
* 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 {
James Housley
committed
result = ssh_easy_statemach(conn);
*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)
{
CURLcode result = CURLE_OK;
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_SESSION_DISCONNECT);
Daniel Stenberg
committed
result = ssh_easy_statemach(conn);
}
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;
bool done = FALSE;
if(status == CURLE_OK) {
James Housley
committed
/* run the state-machine */
if(conn->data->state.used_interface == Curl_if_multi) {
result = ssh_multi_statemach(conn, &done);
}
else {
James Housley
committed
result = ssh_easy_statemach(conn);
done = TRUE;
}
}
else {
James Housley
committed
result = status;
done = TRUE;
}
if(done) {
Daniel Stenberg
committed
struct SSHPROTO *sftp_scp = conn->data->state.proto.ssh;
Daniel Stenberg
committed
Curl_safefree(sftp_scp->path);
sftp_scp->path = NULL;
James Housley
committed
Curl_pgrsDone(conn);
}
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 */
ssize_t Curl_scp_send(struct connectdata *conn, int sockindex,
const void *mem, size_t len)
{
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);
if(nwrite == LIBSSH2_ERROR_EAGAIN)
return nwrite;
}
/*
* If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
* a regular CURLcode value.
*/
ssize_t Curl_scp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len)
{
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);
return nread;
}
/*
* =============== SFTP ===============
*/
James Housley
committed
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
/*
***********************************************************************
*
* 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 {
James Housley
committed
result = ssh_easy_statemach(conn);
*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)
CURLcode result = CURLE_OK;
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);
}
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) {
James Housley
committed
/* 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 */
ssize_t Curl_sftp_send(struct connectdata *conn, int sockindex,
const void *mem, size_t len)
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);
if(nwrite == LIBSSH2_ERROR_EAGAIN)
return nwrite;
}
* Return number of received (decrypted) bytes
*/
ssize_t Curl_sftp_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len)
{
ssize_t nread;
(void)sockindex;
nread = libssh2_sftp_read(conn->proto.sshc.sftp_handle, mem, len);
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;
u_int i, j;
static const char * const 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 */
i++;
(*path)[j] = '\0';
break;
}
if(cp[i] == '\0') { /* End of string */
/*error("Unterminated quote");*/
goto fail;
}
if(cp[i] == '\\') { /* Escaped characters */
if(cp[i] != '\'' && cp[i] != '\"' &&
cp[i] != '\\') {
/*error("Bad escaped character '\\%c'",
cp[i]);*/
goto fail;
}
}
(*path)[j++] = cp[i];
}
if(j == 0) {
/*error("Empty quotes");*/
goto fail;
}
*cpp = cp + i + strspn(cp + i, WHITESPACE);
}
else {
/* Read to end of filename */
end = strpbrk(cp, WHITESPACE);
if(end == NULL)
end = strchr(cp, '\0');
*cpp = end + strspn(end, WHITESPACE);
memcpy(*path, cp, end - cp);
(*path)[end - cp] = '\0';
}
fail:
James Housley
committed
Curl_safefree(*path);
*path = NULL;
return CURLE_QUOTE_ERROR;
}
static const char *sftp_libssh2_strerror(unsigned long err)
{
switch (err) {
James Housley
committed
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
case LIBSSH2_FX_NO_SUCH_FILE:
return "No such file or directory";
case LIBSSH2_FX_PERMISSION_DENIED:
return "Permission denied";
case LIBSSH2_FX_FAILURE:
return "Operation failed";
case LIBSSH2_FX_BAD_MESSAGE:
return "Bad message from SFTP server";
case LIBSSH2_FX_NO_CONNECTION:
return "Not connected to SFTP server";
case LIBSSH2_FX_CONNECTION_LOST:
return "Connection to SFTP server lost";
case LIBSSH2_FX_OP_UNSUPPORTED:
return "Operation not supported by SFTP server";
case LIBSSH2_FX_INVALID_HANDLE:
return "Invalid handle";
case LIBSSH2_FX_NO_SUCH_PATH:
return "No such file or directory";
case LIBSSH2_FX_FILE_ALREADY_EXISTS:
return "File already exists";
case LIBSSH2_FX_WRITE_PROTECT:
return "File is write protected";
case LIBSSH2_FX_NO_MEDIA:
return "No media";
case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
return "Disk full";
case LIBSSH2_FX_QUOTA_EXCEEDED:
return "User quota exceeded";
case LIBSSH2_FX_UNKNOWN_PRINCIPLE:
return "Unknown principle";
case LIBSSH2_FX_LOCK_CONFlICT:
return "File lock conflict";
case LIBSSH2_FX_DIR_NOT_EMPTY:
return "Directory not empty";
case LIBSSH2_FX_NOT_A_DIRECTORY:
return "Not a directory";
case LIBSSH2_FX_INVALID_FILENAME:
return "Invalid filename";
case LIBSSH2_FX_LINK_LOOP:
return "Link points to itself";
}
return "Unknown error in libssh2";
}
#endif /* USE_LIBSSH2 */