summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/vssh
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/vssh')
-rw-r--r--libs/libcurl/src/vssh/curl_path.c42
-rw-r--r--libs/libcurl/src/vssh/curl_path.h4
-rw-r--r--libs/libcurl/src/vssh/libssh.c1714
-rw-r--r--libs/libcurl/src/vssh/libssh2.c2933
-rw-r--r--libs/libcurl/src/vssh/ssh.h73
-rw-r--r--libs/libcurl/src/vssh/wolfssh.c256
6 files changed, 2753 insertions, 2269 deletions
diff --git a/libs/libcurl/src/vssh/curl_path.c b/libs/libcurl/src/vssh/curl_path.c
index 7cf09022e0..f6837e7c9a 100644
--- a/libs/libcurl/src/vssh/curl_path.c
+++ b/libs/libcurl/src/vssh/curl_path.c
@@ -22,15 +22,15 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_SSH)
#include "curl_path.h"
#include <curl/curl.h>
-#include "curl_memory.h"
-#include "escape.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../escape.h"
+#include "../memdebug.h"
#define MAX_SSHPATH_LEN 100000 /* arbitrary */
@@ -50,13 +50,13 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
return result;
/* new path to switch to in case we need to */
- Curl_dyn_init(&npath, MAX_SSHPATH_LEN);
+ curlx_dyn_init(&npath, MAX_SSHPATH_LEN);
/* Check for /~/, indicating relative to the user's home directory */
if((data->conn->handler->protocol & CURLPROTO_SCP) &&
(working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) {
/* It is referenced to the home directory, so strip the leading '/~/' */
- if(Curl_dyn_addn(&npath, &working_path[3], working_path_len - 3)) {
+ if(curlx_dyn_addn(&npath, &working_path[3], working_path_len - 3)) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
@@ -64,7 +64,7 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
else if((data->conn->handler->protocol & CURLPROTO_SFTP) &&
(!strcmp("/~", working_path) ||
((working_path_len > 2) && !memcmp(working_path, "/~/", 3)))) {
- if(Curl_dyn_add(&npath, homedir)) {
+ if(curlx_dyn_add(&npath, homedir)) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
@@ -73,24 +73,24 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
const char *p;
int copyfrom = 3;
/* Copy a separating '/' if homedir does not end with one */
- len = Curl_dyn_len(&npath);
- p = Curl_dyn_ptr(&npath);
+ len = curlx_dyn_len(&npath);
+ p = curlx_dyn_ptr(&npath);
if(len && (p[len-1] != '/'))
copyfrom = 2;
- if(Curl_dyn_addn(&npath,
- &working_path[copyfrom], working_path_len - copyfrom)) {
+ if(curlx_dyn_addn(&npath, &working_path[copyfrom],
+ working_path_len - copyfrom)) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
}
}
- if(Curl_dyn_len(&npath)) {
+ if(curlx_dyn_len(&npath)) {
free(working_path);
/* store the pointer for the caller to receive */
- *path = Curl_dyn_ptr(&npath);
+ *path = curlx_dyn_ptr(&npath);
}
else
*path = working_path;
@@ -133,7 +133,7 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
if(!*cp || !homedir)
return CURLE_QUOTE_ERROR;
- Curl_dyn_init(&out, MAX_PATHLENGTH);
+ curlx_dyn_init(&out, MAX_PATHLENGTH);
/* Ignore leading whitespace */
cp += strspn(cp, WHITESPACE);
@@ -158,12 +158,12 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
goto fail;
}
}
- result = Curl_dyn_addn(&out, &cp[i], 1);
+ result = curlx_dyn_addn(&out, &cp[i], 1);
if(result)
return result;
}
- if(!Curl_dyn_len(&out))
+ if(!curlx_dyn_len(&out))
goto fail;
/* return pointer to second parameter if it exists */
@@ -180,23 +180,23 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
/* Handling for relative path - prepend home directory */
if(cp[0] == '/' && cp[1] == '~' && cp[2] == '/') {
- result = Curl_dyn_add(&out, homedir);
+ result = curlx_dyn_add(&out, homedir);
if(!result)
- result = Curl_dyn_addn(&out, "/", 1);
+ result = curlx_dyn_addn(&out, "/", 1);
if(result)
return result;
cp += 3;
}
/* Copy path name up until first "whitespace" */
- result = Curl_dyn_addn(&out, cp, (end - cp));
+ result = curlx_dyn_addn(&out, cp, (end - cp));
if(result)
return result;
}
- *path = Curl_dyn_ptr(&out);
+ *path = curlx_dyn_ptr(&out);
return CURLE_OK;
fail:
- Curl_dyn_free(&out);
+ curlx_dyn_free(&out);
return CURLE_QUOTE_ERROR;
}
diff --git a/libs/libcurl/src/vssh/curl_path.h b/libs/libcurl/src/vssh/curl_path.h
index 4bac618e13..93c98bde95 100644
--- a/libs/libcurl/src/vssh/curl_path.h
+++ b/libs/libcurl/src/vssh/curl_path.h
@@ -24,9 +24,9 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#include <curl/curl.h>
-#include "urldata.h"
+#include "../urldata.h"
CURLcode Curl_getworkingpath(struct Curl_easy *data,
char *homedir,
diff --git a/libs/libcurl/src/vssh/libssh.c b/libs/libcurl/src/vssh/libssh.c
index b1e9e79396..8de13c5e26 100644
--- a/libs/libcurl/src/vssh/libssh.c
+++ b/libs/libcurl/src/vssh/libssh.c
@@ -25,7 +25,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_LIBSSH
@@ -46,29 +46,29 @@
#endif
#include <curl/curl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "progress.h"
-#include "transfer.h"
-#include "escape.h"
-#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../hostip.h"
+#include "../progress.h"
+#include "../transfer.h"
+#include "../escape.h"
+#include "../http.h" /* for HTTP proxy tunnel stuff */
#include "ssh.h"
-#include "url.h"
-#include "speedcheck.h"
-#include "getinfo.h"
-#include "strdup.h"
-#include "strcase.h"
-#include "vtls/vtls.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "inet_ntop.h"
-#include "parsedate.h" /* for the week day and month names */
-#include "sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "strtoofft.h"
-#include "multiif.h"
-#include "select.h"
-#include "warnless.h"
+#include "../url.h"
+#include "../speedcheck.h"
+#include "../getinfo.h"
+#include "../strdup.h"
+#include "../strcase.h"
+#include "../vtls/vtls.h"
+#include "../cfilters.h"
+#include "../connect.h"
+#include "../inet_ntop.h"
+#include "../parsedate.h" /* for the week day and month names */
+#include "../sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "../curlx/strparse.h"
+#include "../multiif.h"
+#include "../select.h"
+#include "../curlx/warnless.h"
#include "curl_path.h"
#ifdef HAVE_SYS_STAT_H
@@ -82,9 +82,9 @@
#endif
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* A recent macro provided by libssh. Or make our own. */
#ifndef SSH_STRING_FREE_CHAR
@@ -130,14 +130,19 @@ CURLcode sftp_perform(struct Curl_easy *data,
bool *connected,
bool *dophase_done);
-static void sftp_quote(struct Curl_easy *data);
-static void sftp_quote_stat(struct Curl_easy *data);
+static void sftp_quote(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp);
+static void sftp_quote_stat(struct Curl_easy *data, struct ssh_conn *sshc);
static int myssh_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *sock);
-static void myssh_block2waitfor(struct connectdata *conn, bool block);
+static void myssh_block2waitfor(struct connectdata *conn,
+ struct ssh_conn *sshc,
+ bool block);
static CURLcode myssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
+static void sshc_cleanup(struct ssh_conn *sshc);
/*
* SCP protocol handler.
@@ -161,6 +166,7 @@ const struct Curl_handler Curl_handler_scp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SCP, /* protocol */
CURLPROTO_SCP, /* family */
@@ -189,6 +195,7 @@ const struct Curl_handler Curl_handler_sftp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */
@@ -221,23 +228,23 @@ static CURLcode sftp_error_to_CURLE(int err)
}
#ifndef DEBUGBUILD
-#define state(x,y) mystate(x,y)
+#define myssh_state(x,y,z) myssh_set_state(x,y,z)
#else
-#define state(x,y) mystate(x,y, __LINE__)
+#define myssh_state(x,y,z) myssh_set_state(x,y,z, __LINE__)
#endif
/*
* SSH State machine related code
*/
/* This is the ONLY way to change SSH state! */
-static void mystate(struct Curl_easy *data, sshstate nowstate
+static void myssh_set_state(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ sshstate nowstate
#ifdef DEBUGBUILD
- , int lineno
+ , int lineno
#endif
)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char *const names[] = {
@@ -310,7 +317,7 @@ static void mystate(struct Curl_easy *data, sshstate nowstate
lineno);
}
#endif
-
+ (void)data;
sshc->state = nowstate;
}
@@ -324,11 +331,9 @@ static void mystate(struct Curl_easy *data, sshstate nowstate
*
* Returns SSH_OK or SSH_ERROR.
*/
-static int myssh_is_known(struct Curl_easy *data)
+static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc)
{
int rc;
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
ssh_key pubkey;
size_t hlen;
unsigned char *hash = NULL;
@@ -340,17 +345,11 @@ static int myssh_is_known(struct Curl_easy *data)
struct curl_khkey *knownkeyp = NULL;
curl_sshkeycallback func =
data->set.ssh_keyfunc;
-
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
struct ssh_knownhosts_entry *knownhostsentry = NULL;
struct curl_khkey knownkey;
-#endif
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey);
-#else
- rc = ssh_get_publickey(sshc->ssh_session, &pubkey);
-#endif
+
if(rc != SSH_OK)
return rc;
@@ -386,7 +385,6 @@ static int myssh_is_known(struct Curl_easy *data)
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
/* Get the known_key from the known hosts file */
vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session,
&knownhostsentry);
@@ -444,22 +442,6 @@ static int myssh_is_known(struct Curl_easy *data)
break;
}
-#else
- vstate = ssh_is_server_known(sshc->ssh_session);
- switch(vstate) {
- case SSH_SERVER_KNOWN_OK:
- keymatch = CURLKHMATCH_OK;
- break;
- case SSH_SERVER_FILE_NOT_FOUND:
- case SSH_SERVER_NOT_KNOWN:
- keymatch = CURLKHMATCH_MISSING;
- break;
- default:
- keymatch = CURLKHMATCH_MISMATCH;
- break;
- }
-#endif
-
if(func) { /* use callback to determine action */
rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64);
if(rc != SSH_OK)
@@ -476,18 +458,14 @@ static int myssh_is_known(struct Curl_easy *data)
foundkey.keytype = CURLKHTYPE_RSA1;
break;
case SSH_KEYTYPE_ECDSA:
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
-#endif
foundkey.keytype = CURLKHTYPE_ECDSA;
break;
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0)
case SSH_KEYTYPE_ED25519:
foundkey.keytype = CURLKHTYPE_ED25519;
break;
-#endif
case SSH_KEYTYPE_DSS:
foundkey.keytype = CURLKHTYPE_DSS;
break;
@@ -504,11 +482,7 @@ static int myssh_is_known(struct Curl_easy *data)
switch(rc) {
case CURLKHSTAT_FINE_ADD_TO_FILE:
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
rc = ssh_session_update_known_hosts(sshc->ssh_session);
-#else
- rc = ssh_write_knownhost(sshc->ssh_session);
-#endif
if(rc != SSH_OK) {
goto cleanup;
}
@@ -539,22 +513,20 @@ cleanup:
if(hash)
ssh_clean_pubkey_hash(&hash);
ssh_key_free(pubkey);
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
if(knownhostsentry) {
ssh_knownhosts_entry_free(knownhostsentry);
}
-#endif
return rc;
}
-#define MOVE_TO_ERROR_STATE(_r) do { \
- state(data, SSH_SESSION_DISCONNECT); \
- sshc->actualcode = _r; \
- rc = SSH_ERROR; \
+#define MOVE_TO_ERROR_STATE(_r) do { \
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT); \
+ sshc->actualcode = _r; \
+ rc = SSH_ERROR; \
} while(0)
#define MOVE_TO_SFTP_CLOSE_STATE() do { \
- state(data, SSH_SFTP_CLOSE); \
+ myssh_state(data, sshc, SSH_SFTP_CLOSE); \
sshc->actualcode = \
sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \
rc = SSH_ERROR; \
@@ -563,7 +535,7 @@ cleanup:
#define MOVE_TO_PASSWD_AUTH do { \
if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \
rc = SSH_OK; \
- state(data, SSH_AUTH_PASS_INIT); \
+ myssh_state(data, sshc, SSH_AUTH_PASS_INIT); \
} \
else { \
MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \
@@ -573,7 +545,7 @@ cleanup:
#define MOVE_TO_KEY_AUTH do { \
if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \
rc = SSH_OK; \
- state(data, SSH_AUTH_KEY_INIT); \
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT); \
} \
else { \
MOVE_TO_PASSWD_AUTH; \
@@ -583,7 +555,7 @@ cleanup:
#define MOVE_TO_GSSAPI_AUTH do { \
if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \
rc = SSH_OK; \
- state(data, SSH_AUTH_GSSAPI); \
+ myssh_state(data, sshc, SSH_AUTH_GSSAPI); \
} \
else { \
MOVE_TO_KEY_AUTH; \
@@ -591,10 +563,10 @@ cleanup:
} while(0)
static
-int myssh_auth_interactive(struct connectdata *conn)
+int myssh_auth_interactive(struct connectdata *conn,
+ struct ssh_conn *sshc)
{
int rc;
- struct ssh_conn *sshc = &conn->proto.sshc;
int nprompts;
restart:
@@ -655,178 +627,493 @@ restart:
return rc;
}
-/*
- * ssh_statemach_act() runs the SSH state machine as far as it can without
- * blocking and without reaching the end. The data the pointer 'block' points
- * to will be set to TRUE if the libssh function returns SSH_AGAIN
- * meaning it wants to be called again when the socket is ready
- */
-static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
+static void myssh_state_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
- CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct SSHPROTO *protop = data->req.p.ssh;
- struct ssh_conn *sshc = &conn->proto.sshc;
- curl_socket_t sock = conn->sock[FIRSTSOCKET];
- int rc = SSH_NO_ERROR, err;
- int seekerr = CURL_SEEKFUNC_OK;
- const char *err_msg;
- *block = 0; /* we are not blocking by default */
-
- do {
-
- switch(sshc->state) {
- case SSH_INIT:
- sshc->secondCreateDirs = 0;
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_OK;
+ sshc->secondCreateDirs = 0;
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_OK;
#if 0
- ssh_set_log_level(SSH_LOG_PROTOCOL);
+ ssh_set_log_level(SSH_LOG_PROTOCOL);
#endif
- /* Set libssh to non-blocking, since everything internally is
- non-blocking */
- ssh_set_blocking(sshc->ssh_session, 0);
+ /* Set libssh to non-blocking, since everything internally is
+ non-blocking */
+ ssh_set_blocking(sshc->ssh_session, 0);
- state(data, SSH_S_STARTUP);
- FALLTHROUGH();
+ myssh_state(data, sshc, SSH_S_STARTUP);
+}
- case SSH_S_STARTUP:
- rc = ssh_connect(sshc->ssh_session);
+static int myssh_state_startup(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ struct connectdata *conn = data->conn;
+ int rc = ssh_connect(sshc->ssh_session);
- myssh_block2waitfor(conn, (rc == SSH_AGAIN));
- if(rc == SSH_AGAIN) {
- DEBUGF(infof(data, "ssh_connect -> EAGAIN"));
- break;
- }
+ myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN));
+ if(rc == SSH_AGAIN) {
+ DEBUGF(infof(data, "ssh_connect -> EAGAIN"));
+ }
+ else if(rc != SSH_OK) {
+ failf(data, "Failure establishing ssh session");
+ MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
+ }
+ else
+ myssh_state(data, sshc, SSH_HOSTKEY);
- if(rc != SSH_OK) {
- failf(data, "Failure establishing ssh session");
- MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
- break;
- }
+ return rc;
+}
- state(data, SSH_HOSTKEY);
+static int myssh_state_authlist(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc;
+ sshc->authed = FALSE;
- FALLTHROUGH();
- case SSH_HOSTKEY:
+ rc = ssh_userauth_none(sshc->ssh_session, NULL);
+ if(rc == SSH_AUTH_AGAIN)
+ return SSH_AGAIN;
+
+ if(rc == SSH_AUTH_SUCCESS) {
+ sshc->authed = TRUE;
+ infof(data, "Authenticated with none");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ return rc;
+ }
+ else if(rc == SSH_AUTH_ERROR) {
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ return rc;
+ }
+
+ sshc->auth_methods =
+ (unsigned int)ssh_userauth_list(sshc->ssh_session, NULL);
+ if(sshc->auth_methods)
+ infof(data, "SSH authentication methods available: %s%s%s%s",
+ sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ?
+ "public key, ": "",
+ sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ?
+ "GSSAPI, " : "",
+ sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ?
+ "keyboard-interactive, " : "",
+ sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ?
+ "password": "");
+ if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
+ myssh_state(data, sshc, SSH_AUTH_PKEY_INIT);
+ infof(data, "Authentication using SSH public key file");
+ }
+ else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
+ myssh_state(data, sshc, SSH_AUTH_GSSAPI);
+ }
+ else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ }
+ else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
+ myssh_state(data, sshc, SSH_AUTH_PASS_INIT);
+ }
+ else { /* unsupported authentication method */
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ }
+ return rc;
+}
+
+static int myssh_state_auth_pkey_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc;
+ if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
+ MOVE_TO_GSSAPI_AUTH;
+ return 0;
+ }
+
+ /* Two choices, (1) private key was given on CMD,
+ * (2) use the "default" keys. */
+ if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
+ if(sshc->pubkey && !data->set.ssl.key_passwd) {
+ rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
+ sshc->pubkey);
+ if(rc == SSH_AUTH_AGAIN)
+ return SSH_AGAIN;
- rc = myssh_is_known(data);
if(rc != SSH_OK) {
- MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
- break;
+ MOVE_TO_GSSAPI_AUTH;
+ return rc;
}
+ }
- state(data, SSH_AUTHLIST);
- FALLTHROUGH();
- case SSH_AUTHLIST:{
- sshc->authed = FALSE;
+ rc = ssh_pki_import_privkey_file(data->
+ set.str[STRING_SSH_PRIVATE_KEY],
+ data->set.ssl.key_passwd, NULL,
+ NULL, &sshc->privkey);
+ if(rc != SSH_OK) {
+ failf(data, "Could not load private key file %s",
+ data->set.str[STRING_SSH_PRIVATE_KEY]);
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ return rc;
+ }
- rc = ssh_userauth_none(sshc->ssh_session, NULL);
- if(rc == SSH_AUTH_AGAIN) {
- rc = SSH_AGAIN;
- break;
- }
+ myssh_state(data, sshc, SSH_AUTH_PKEY);
+ }
+ else {
+ rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
+ data->set.ssl.key_passwd);
+ if(rc == SSH_AUTH_AGAIN)
+ return SSH_AGAIN;
+
+ if(rc == SSH_AUTH_SUCCESS) {
+ rc = SSH_OK;
+ sshc->authed = TRUE;
+ infof(data, "Completed public key authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ return rc;
+ }
- if(rc == SSH_AUTH_SUCCESS) {
- sshc->authed = TRUE;
- infof(data, "Authenticated with none");
- state(data, SSH_AUTH_DONE);
- break;
- }
- else if(rc == SSH_AUTH_ERROR) {
- MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
- break;
- }
+ MOVE_TO_GSSAPI_AUTH;
+ }
+ return rc;
+}
- sshc->auth_methods =
- (unsigned int)ssh_userauth_list(sshc->ssh_session, NULL);
- if(sshc->auth_methods)
- infof(data, "SSH authentication methods available: %s%s%s%s",
- sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ?
- "public key, ": "",
- sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ?
- "GSSAPI, " : "",
- sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ?
- "keyboard-interactive, " : "",
- sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ?
- "password": "");
- if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
- state(data, SSH_AUTH_PKEY_INIT);
- infof(data, "Authentication using SSH public key file");
- }
- else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
- state(data, SSH_AUTH_GSSAPI);
- }
- else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
- state(data, SSH_AUTH_KEY_INIT);
- }
- else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
- state(data, SSH_AUTH_PASS_INIT);
- }
- else { /* unsupported authentication method */
- MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
- break;
+static int myssh_state_upload_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ int flags;
+ int rc = 0;
+
+ if(data->state.resume_from) {
+ sftp_attributes attrs;
+
+ if(data->state.resume_from < 0) {
+ attrs = sftp_stat(sshc->sftp_session, sshp->path);
+ if(attrs) {
+ curl_off_t size = attrs->size;
+ if(size < 0) {
+ failf(data, "Bad file size (%" FMT_OFF_T ")", size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
}
+ data->state.resume_from = attrs->size;
- break;
+ sftp_attributes_free(attrs);
}
- case SSH_AUTH_PKEY_INIT:
- if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
- MOVE_TO_GSSAPI_AUTH;
- break;
+ else {
+ data->state.resume_from = 0;
}
+ }
+ }
- /* Two choices, (1) private key was given on CMD,
- * (2) use the "default" keys. */
- if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
- if(sshc->pubkey && !data->set.ssl.key_passwd) {
- rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
- sshc->pubkey);
- if(rc == SSH_AUTH_AGAIN) {
- rc = SSH_AGAIN;
- break;
- }
+ if(data->set.remote_append)
+ /* Try to open for append, but create if nonexisting */
+ flags = O_WRONLY|O_CREAT|O_APPEND;
+ else if(data->state.resume_from > 0)
+ /* If we have restart position then open for append */
+ flags = O_WRONLY|O_APPEND;
+ else
+ /* Clear file before writing (normal behavior) */
+ flags = O_WRONLY|O_CREAT|O_TRUNC;
+
+ if(sshc->sftp_file)
+ sftp_close(sshc->sftp_file);
+ sshc->sftp_file =
+ sftp_open(sshc->sftp_session, sshp->path,
+ flags, (mode_t)data->set.new_file_perms);
+ if(!sshc->sftp_file) {
+ int err = sftp_get_error(sshc->sftp_session);
+
+ if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
+ err == SSH_FX_NO_SUCH_PATH)) &&
+ (data->set.ftp_create_missing_dirs &&
+ (strlen(sshp->path) > 1))) {
+ /* try to create the path remotely */
+ rc = 0;
+ sshc->secondCreateDirs = 1;
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_INIT);
+ return rc;
+ }
+ else {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ return rc;
+ }
+ }
- if(rc != SSH_OK) {
- MOVE_TO_GSSAPI_AUTH;
- break;
- }
- }
+ /* If we have a restart point then we need to seek to the correct
+ position. */
+ if(data->state.resume_from > 0) {
+ int seekerr = CURL_SEEKFUNC_OK;
+ /* Let's read off the proper amount of bytes from the input. */
+ if(data->set.seek_func) {
+ Curl_set_in_callback(data, TRUE);
+ seekerr = data->set.seek_func(data->set.seek_client,
+ data->state.resume_from, SEEK_SET);
+ Curl_set_in_callback(data, FALSE);
+ }
- rc = ssh_pki_import_privkey_file(data->
- set.str[STRING_SSH_PRIVATE_KEY],
- data->set.ssl.key_passwd, NULL,
- NULL, &sshc->privkey);
- if(rc != SSH_OK) {
- failf(data, "Could not load private key file %s",
- data->set.str[STRING_SSH_PRIVATE_KEY]);
- MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
- break;
+ if(seekerr != CURL_SEEKFUNC_OK) {
+ curl_off_t passed = 0;
+
+ if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+ failf(data, "Could not seek stream");
+ MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
+ return rc;
+ }
+ /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
+ do {
+ char scratch[4*1024];
+ size_t readthisamountnow =
+ (data->state.resume_from - passed >
+ (curl_off_t)sizeof(scratch)) ?
+ sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
+
+ size_t actuallyread =
+ data->state.fread_func(scratch, 1,
+ readthisamountnow, data->state.in);
+
+ passed += actuallyread;
+ if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+ /* this checks for greater-than only to make sure that the
+ CURL_READFUNC_ABORT return code still aborts */
+ failf(data, "Failed to read data");
+ MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
+ return rc;
}
+ } while(passed < data->state.resume_from);
+ }
- state(data, SSH_AUTH_PKEY);
- break;
+ /* now, decrease the size of the read */
+ if(data->state.infilesize > 0) {
+ data->state.infilesize -= data->state.resume_from;
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+ rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
+ if(rc) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ return rc;
+ }
+ }
+ if(data->state.infilesize > 0) {
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+ /* upload data */
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
+
+ /* not set by Curl_xfer_setup to preserve keepon bits */
+ data->conn->sockfd = data->conn->writesockfd;
+
+ /* store this original bitmask setup to use later on if we cannot
+ figure out a "real" bitmask */
+ sshc->orig_waitfor = data->req.keepon;
+
+ /* we want to use the _sending_ function even when the socket turns
+ out readable as the underlying libssh sftp send function will deal
+ with both accordingly */
+ data->state.select_bits = CURL_CSELECT_OUT;
+
+ /* since we do not really wait for anything at this point, we want the
+ state machine to move on as soon as possible so we set a very short
+ timeout here */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
+ sshc->sftp_send_state = 0;
+#endif
+ myssh_state(data, sshc, SSH_STOP);
+ return rc;
+}
+
+static int myssh_state_sftp_download_stat(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ curl_off_t size;
+ int rc = 0;
+ sftp_attributes attrs = sftp_fstat(sshc->sftp_file);
+ if(!attrs ||
+ !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
+ (attrs->size == 0)) {
+ /*
+ * sftp_fstat did not return an error, so maybe the server
+ * just does not support stat()
+ * OR the server does not return a file size with a stat()
+ * OR file size is 0
+ */
+ data->req.size = -1;
+ data->req.maxdownload = -1;
+ Curl_pgrsSetDownloadSize(data, -1);
+ size = 0;
+ if(attrs)
+ sftp_attributes_free(attrs);
+ }
+ else {
+ size = attrs->size;
+
+ sftp_attributes_free(attrs);
+
+ if(size < 0) {
+ failf(data, "Bad file size (%" FMT_OFF_T ")", size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
+ }
+ if(data->state.use_range) {
+ curl_off_t from, to;
+ const char *p = data->state.range;
+ int from_t, to_t;
+
+ from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX);
+ if(from_t == STRE_OVERFLOW) {
+ MOVE_TO_ERROR_STATE(CURLE_RANGE_ERROR);
+ return rc;
+ }
+ curlx_str_passblanks(&p);
+ (void)curlx_str_single(&p, '-');
+
+ to_t = curlx_str_numblanks(&p, &to);
+ if(to_t == STRE_OVERFLOW)
+ return CURLE_RANGE_ERROR;
+
+ if((to_t == STRE_NO_NUM) || (to >= size)) {
+ to = size - 1;
+ to_t = STRE_OK;
+ }
+
+ if(from_t == STRE_NO_NUM) {
+ /* from is relative to end of file */
+ from = size - to;
+ to = size - 1;
+ from_t = STRE_OK;
+ }
+ if(from > size) {
+ failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+ FMT_OFF_T ")", from, size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
+ }
+ if(from > to) {
+ from = to;
+ size = 0;
}
else {
- rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
- data->set.ssl.key_passwd);
- if(rc == SSH_AUTH_AGAIN) {
- rc = SSH_AGAIN;
- break;
- }
- if(rc == SSH_AUTH_SUCCESS) {
- rc = SSH_OK;
- sshc->authed = TRUE;
- infof(data, "Completed public key authentication");
- state(data, SSH_AUTH_DONE);
- break;
+ if((to - from) == CURL_OFF_T_MAX) {
+ MOVE_TO_ERROR_STATE(CURLE_RANGE_ERROR);
+ return rc;
}
+ size = to - from + 1;
+ }
- MOVE_TO_GSSAPI_AUTH;
+ rc = sftp_seek64(sshc->sftp_file, from);
+ if(rc) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ return rc;
+ }
+ }
+ data->req.size = size;
+ data->req.maxdownload = size;
+ 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) {
+ /* We are supposed to download the last abs(from) bytes */
+ if((curl_off_t)size < -data->state.resume_from) {
+ failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+ FMT_OFF_T ")", data->state.resume_from, size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
+ }
+ /* download from where? */
+ data->state.resume_from += size;
+ }
+ else {
+ if((curl_off_t)size < data->state.resume_from) {
+ failf(data, "Offset (%" FMT_OFF_T
+ ") was beyond file size (%" FMT_OFF_T ")",
+ data->state.resume_from, size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
+ }
+ }
+ /* Now store the number of bytes we are expected to download */
+ data->req.size = size - data->state.resume_from;
+ data->req.maxdownload = size - data->state.resume_from;
+ Curl_pgrsSetDownloadSize(data,
+ size - data->state.resume_from);
+
+ rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
+ if(rc) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ return rc;
+ }
+ }
+
+ /* Setup the actual download */
+ if(data->req.size == 0) {
+ /* no data to transfer */
+ Curl_xfer_setup_nop(data);
+ infof(data, "File already completely downloaded");
+ myssh_state(data, sshc, SSH_STOP);
+ return rc;
+ }
+ Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
+
+ /* not set by Curl_xfer_setup to preserve keepon bits */
+ data->conn->writesockfd = data->conn->sockfd;
+
+ /* we want to use the _receiving_ function even when the socket turns
+ out writableable as the underlying libssh recv function will deal
+ with both accordingly */
+ data->state.select_bits = CURL_CSELECT_IN;
+
+ sshc->sftp_recv_state = 0;
+ myssh_state(data, sshc, SSH_STOP);
+
+ return rc;
+}
+
+/*
+ * ssh_statemach_act() runs the SSH state machine as far as it can without
+ * blocking and without reaching the end. The data the pointer 'block' points
+ * to will be set to TRUE if the libssh function returns SSH_AGAIN
+ * meaning it wants to be called again when the socket is ready
+ */
+static CURLcode myssh_statemach_act(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
+ bool *block)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int rc = SSH_NO_ERROR, err;
+ const char *err_msg;
+ *block = 0; /* we are not blocking by default */
+
+ do {
+
+ switch(sshc->state) {
+ case SSH_INIT:
+ myssh_state_init(data, sshc);
+ FALLTHROUGH();
+
+ case SSH_S_STARTUP:
+ rc = myssh_state_startup(data, sshc);
+ if(rc)
+ break;
+ FALLTHROUGH();
+ case SSH_HOSTKEY:
+ rc = myssh_is_known(data, sshc);
+ if(rc != SSH_OK) {
+ MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
+ break;
}
+
+ myssh_state(data, sshc, SSH_AUTHLIST);
+ FALLTHROUGH();
+ case SSH_AUTHLIST:
+ rc = myssh_state_authlist(data, sshc);
+ break;
+ case SSH_AUTH_PKEY_INIT:
+ rc = myssh_state_auth_pkey_init(data, sshc);
break;
case SSH_AUTH_PKEY:
rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey);
@@ -838,7 +1125,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(rc == SSH_AUTH_SUCCESS) {
sshc->authed = TRUE;
infof(data, "Completed public key authentication");
- state(data, SSH_AUTH_DONE);
+ myssh_state(data, sshc, SSH_AUTH_DONE);
break;
}
else {
@@ -863,7 +1150,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
rc = SSH_OK;
sshc->authed = TRUE;
infof(data, "Completed gssapi authentication");
- state(data, SSH_AUTH_DONE);
+ myssh_state(data, sshc, SSH_AUTH_DONE);
break;
}
@@ -872,7 +1159,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
case SSH_AUTH_KEY_INIT:
if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) {
- state(data, SSH_AUTH_KEY);
+ myssh_state(data, sshc, SSH_AUTH_KEY);
}
else {
MOVE_TO_PASSWD_AUTH;
@@ -881,14 +1168,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
case SSH_AUTH_KEY:
/* keyboard-interactive authentication */
- rc = myssh_auth_interactive(conn);
+ rc = myssh_auth_interactive(conn, sshc);
if(rc == SSH_AGAIN) {
break;
}
if(rc == SSH_OK) {
sshc->authed = TRUE;
infof(data, "completed keyboard interactive authentication");
- state(data, SSH_AUTH_DONE);
+ myssh_state(data, sshc, SSH_AUTH_DONE);
}
else {
MOVE_TO_PASSWD_AUTH;
@@ -900,7 +1187,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
break;
}
- state(data, SSH_AUTH_PASS);
+ myssh_state(data, sshc, SSH_AUTH_PASS);
FALLTHROUGH();
case SSH_AUTH_PASS:
@@ -913,7 +1200,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(rc == SSH_AUTH_SUCCESS) {
sshc->authed = TRUE;
infof(data, "Completed password authentication");
- state(data, SSH_AUTH_DONE);
+ myssh_state(data, sshc, SSH_AUTH_DONE);
}
else {
MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
@@ -938,11 +1225,11 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
conn->writesockfd = CURL_SOCKET_BAD;
if(conn->handler->protocol == CURLPROTO_SFTP) {
- state(data, SSH_SFTP_INIT);
+ myssh_state(data, sshc, SSH_SFTP_INIT);
break;
}
infof(data, "SSH CONNECT phase done");
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_SFTP_INIT:
@@ -963,7 +1250,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE));
break;
}
- state(data, SSH_SFTP_REALPATH);
+ myssh_state(data, sshc, SSH_SFTP_REALPATH);
FALLTHROUGH();
case SSH_SFTP_REALPATH:
/*
@@ -974,31 +1261,34 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
break;
}
- data->state.most_recent_ftp_entrypath = sshc->homedir;
+ free(data->state.most_recent_ftp_entrypath);
+ data->state.most_recent_ftp_entrypath = strdup(sshc->homedir);
+ if(!data->state.most_recent_ftp_entrypath)
+ return CURLE_OUT_OF_MEMORY;
/* This is the last step in the SFTP connect phase. Do note that while
we get the homedir here, we get the "workingpath" in the DO action
since the homedir will remain the same between request but the
working path will not. */
DEBUGF(infof(data, "SSH CONNECT phase done"));
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_SFTP_QUOTE_INIT:
- result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
+ result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
if(result) {
sshc->actualcode = result;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
if(data->set.quote) {
infof(data, "Sending quote commands");
sshc->quote_item = data->set.quote;
- state(data, SSH_SFTP_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
}
else {
- state(data, SSH_SFTP_GETINFO);
+ myssh_state(data, sshc, SSH_SFTP_GETINFO);
}
break;
@@ -1006,16 +1296,16 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(data->set.postquote) {
infof(data, "Sending quote commands");
sshc->quote_item = data->set.postquote;
- state(data, SSH_SFTP_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
}
else {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
}
break;
case SSH_SFTP_QUOTE:
/* Send any quote commands */
- sftp_quote(data);
+ sftp_quote(data, sshc, sshp);
break;
case SSH_SFTP_NEXT_QUOTE:
@@ -1025,21 +1315,21 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
sshc->quote_item = sshc->quote_item->next;
if(sshc->quote_item) {
- state(data, SSH_SFTP_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
}
else {
if(sshc->nextstate != SSH_NO_STATE) {
- state(data, sshc->nextstate);
+ myssh_state(data, sshc, sshc->nextstate);
sshc->nextstate = SSH_NO_STATE;
}
else {
- state(data, SSH_SFTP_GETINFO);
+ myssh_state(data, sshc, SSH_SFTP_GETINFO);
}
}
break;
case SSH_SFTP_QUOTE_STAT:
- sftp_quote_stat(data);
+ sftp_quote_stat(data, sshc);
break;
case SSH_SFTP_QUOTE_SETSTAT:
@@ -1050,7 +1340,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path2);
failf(data, "Attempt to set SFTP stats failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
/* sshc->actualcode = sftp_error_to_CURLE(err);
@@ -1058,7 +1348,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
* the error the libssh2 backend is returning */
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_SYMLINK:
@@ -1069,12 +1359,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path2);
failf(data, "symlink command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_MKDIR:
@@ -1084,12 +1374,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path1);
failf(data, "mkdir command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_RENAME:
@@ -1100,12 +1390,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path2);
failf(data, "rename command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_RMDIR:
@@ -1114,12 +1404,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path1);
failf(data, "rmdir command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_UNLINK:
@@ -1128,12 +1418,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path1);
failf(data, "rm command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_STATVFS:
@@ -1145,7 +1435,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path1);
failf(data, "statvfs command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
@@ -1178,7 +1468,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(!tmp) {
result = CURLE_OUT_OF_MEMORY;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
break;
}
@@ -1186,21 +1476,21 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
free(tmp);
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
}
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
}
case SSH_SFTP_GETINFO:
if(data->set.get_filetime) {
- state(data, SSH_SFTP_FILETIME);
+ myssh_state(data, sshc, SSH_SFTP_FILETIME);
}
else {
- state(data, SSH_SFTP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
}
break;
@@ -1208,178 +1498,38 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
{
sftp_attributes attrs;
- attrs = sftp_stat(sshc->sftp_session, protop->path);
+ attrs = sftp_stat(sshc->sftp_session, sshp->path);
if(attrs) {
data->info.filetime = attrs->mtime;
sftp_attributes_free(attrs);
}
- state(data, SSH_SFTP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
break;
}
case SSH_SFTP_TRANS_INIT:
if(data->state.upload)
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
else {
- if(protop->path[strlen(protop->path)-1] == '/')
- state(data, SSH_SFTP_READDIR_INIT);
+ if(sshp->path[strlen(sshp->path)-1] == '/')
+ myssh_state(data, sshc, SSH_SFTP_READDIR_INIT);
else
- state(data, SSH_SFTP_DOWNLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
}
break;
case SSH_SFTP_UPLOAD_INIT:
- {
- int flags;
-
- if(data->state.resume_from) {
- sftp_attributes attrs;
-
- if(data->state.resume_from < 0) {
- attrs = sftp_stat(sshc->sftp_session, protop->path);
- if(attrs) {
- curl_off_t size = attrs->size;
- if(size < 0) {
- failf(data, "Bad file size (%" FMT_OFF_T ")", size);
- MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
- break;
- }
- data->state.resume_from = attrs->size;
-
- sftp_attributes_free(attrs);
- }
- else {
- data->state.resume_from = 0;
- }
- }
- }
-
- if(data->set.remote_append)
- /* Try to open for append, but create if nonexisting */
- flags = O_WRONLY|O_CREAT|O_APPEND;
- else if(data->state.resume_from > 0)
- /* If we have restart position then open for append */
- flags = O_WRONLY|O_APPEND;
- else
- /* Clear file before writing (normal behavior) */
- flags = O_WRONLY|O_CREAT|O_TRUNC;
-
- if(sshc->sftp_file)
- sftp_close(sshc->sftp_file);
- sshc->sftp_file =
- sftp_open(sshc->sftp_session, protop->path,
- flags, (mode_t)data->set.new_file_perms);
- if(!sshc->sftp_file) {
- err = sftp_get_error(sshc->sftp_session);
-
- if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
- err == SSH_FX_NO_SUCH_PATH)) &&
- (data->set.ftp_create_missing_dirs &&
- (strlen(protop->path) > 1))) {
- /* try to create the path remotely */
- rc = 0;
- sshc->secondCreateDirs = 1;
- state(data, SSH_SFTP_CREATE_DIRS_INIT);
- break;
- }
- else {
- MOVE_TO_SFTP_CLOSE_STATE();
- break;
- }
- }
-
- /* If we have a restart point then we need to seek to the correct
- position. */
- if(data->state.resume_from > 0) {
- /* Let's read off the proper amount of bytes from the input. */
- if(data->set.seek_func) {
- Curl_set_in_callback(data, TRUE);
- seekerr = data->set.seek_func(data->set.seek_client,
- data->state.resume_from, SEEK_SET);
- Curl_set_in_callback(data, FALSE);
- }
-
- if(seekerr != CURL_SEEKFUNC_OK) {
- curl_off_t passed = 0;
-
- if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
- failf(data, "Could not seek stream");
- return CURLE_FTP_COULDNT_USE_REST;
- }
- /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
- do {
- char scratch[4*1024];
- size_t readthisamountnow =
- (data->state.resume_from - passed >
- (curl_off_t)sizeof(scratch)) ?
- sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
-
- size_t actuallyread =
- data->state.fread_func(scratch, 1,
- readthisamountnow, data->state.in);
-
- passed += actuallyread;
- if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
- /* this checks for greater-than only to make sure that the
- CURL_READFUNC_ABORT return code still aborts */
- failf(data, "Failed to read data");
- MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
- break;
- }
- } while(passed < data->state.resume_from);
- if(rc)
- break;
- }
-
- /* now, decrease the size of the read */
- if(data->state.infilesize > 0) {
- data->state.infilesize -= data->state.resume_from;
- data->req.size = data->state.infilesize;
- Curl_pgrsSetUploadSize(data, data->state.infilesize);
- }
-
- rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
- if(rc) {
- MOVE_TO_SFTP_CLOSE_STATE();
- break;
- }
- }
- if(data->state.infilesize > 0) {
- data->req.size = data->state.infilesize;
- Curl_pgrsSetUploadSize(data, data->state.infilesize);
- }
- /* upload data */
- Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
-
- /* not set by Curl_xfer_setup to preserve keepon bits */
- conn->sockfd = conn->writesockfd;
-
- /* store this original bitmask setup to use later on if we cannot
- figure out a "real" bitmask */
- sshc->orig_waitfor = data->req.keepon;
-
- /* we want to use the _sending_ function even when the socket turns
- out readable as the underlying libssh sftp send function will deal
- with both accordingly */
- data->state.select_bits = CURL_CSELECT_OUT;
-
- /* since we do not really wait for anything at this point, we want the
- state machine to move on as soon as possible so we set a very short
- timeout here */
- Curl_expire(data, 0, EXPIRE_RUN_NOW);
-
- state(data, SSH_STOP);
+ rc = myssh_state_upload_init(data, sshc, sshp);
break;
- }
case SSH_SFTP_CREATE_DIRS_INIT:
- if(strlen(protop->path) > 1) {
- sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */
- state(data, SSH_SFTP_CREATE_DIRS);
+ if(strlen(sshp->path) > 1) {
+ sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
}
else {
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
}
break;
@@ -1388,16 +1538,16 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(sshc->slash_pos) {
*sshc->slash_pos = 0;
- infof(data, "Creating directory '%s'", protop->path);
- state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
+ infof(data, "Creating directory '%s'", sshp->path);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
break;
}
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
break;
case SSH_SFTP_CREATE_DIRS_MKDIR:
/* 'mode' - parameter is preliminary - default to 0644 */
- rc = sftp_mkdir(sshc->sftp_session, protop->path,
+ rc = sftp_mkdir(sshc->sftp_session, sshp->path,
(mode_t)data->set.new_directory_perms);
*sshc->slash_pos = '/';
++sshc->slash_pos;
@@ -1416,13 +1566,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
rc = 0; /* clear rc and continue */
}
- state(data, SSH_SFTP_CREATE_DIRS);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
break;
case SSH_SFTP_READDIR_INIT:
Curl_pgrsSetDownloadSize(data, -1);
if(data->req.no_body) {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
@@ -1431,18 +1581,18 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
* listing
*/
sshc->sftp_dir = sftp_opendir(sshc->sftp_session,
- protop->path);
+ sshp->path);
if(!sshc->sftp_dir) {
failf(data, "Could not open directory for reading: %s",
ssh_get_error(sshc->ssh_session));
MOVE_TO_SFTP_CLOSE_STATE();
break;
}
- state(data, SSH_SFTP_READDIR);
+ myssh_state(data, sshc, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR:
- Curl_dyn_reset(&sshc->readdir_buf);
+ curlx_dyn_reset(&sshc->readdir_buf);
if(sshc->readdir_attrs)
sftp_attributes_free(sshc->readdir_attrs);
@@ -1457,7 +1607,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
tmpLine = aprintf("%s\n", sshc->readdir_filename);
if(!tmpLine) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
@@ -1466,39 +1616,39 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
free(tmpLine);
if(result) {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
}
else {
- if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
+ if(curlx_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
sshc->actualcode = CURLE_OUT_OF_MEMORY;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
((sshc->readdir_attrs->permissions & SSH_S_IFMT) ==
SSH_S_IFLNK)) {
- sshc->readdir_linkPath = aprintf("%s%s", protop->path,
+ sshc->readdir_linkPath = aprintf("%s%s", sshp->path,
sshc->readdir_filename);
if(!sshc->readdir_linkPath) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
- state(data, SSH_SFTP_READDIR_LINK);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_LINK);
break;
}
- state(data, SSH_SFTP_READDIR_BOTTOM);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
break;
}
}
else if(sftp_dir_eof(sshc->sftp_dir)) {
- state(data, SSH_SFTP_READDIR_DONE);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_DONE);
break;
}
else {
@@ -1540,8 +1690,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->readdir_linkPath);
- if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s",
- sshc->readdir_filename)) {
+ if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s",
+ sshc->readdir_filename)) {
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
@@ -1551,24 +1701,24 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
sshc->readdir_filename = NULL;
sshc->readdir_longentry = NULL;
- state(data, SSH_SFTP_READDIR_BOTTOM);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
FALLTHROUGH();
case SSH_SFTP_READDIR_BOTTOM:
- if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1))
+ if(curlx_dyn_addn(&sshc->readdir_buf, "\n", 1))
result = CURLE_OUT_OF_MEMORY;
else
result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&sshc->readdir_buf),
- Curl_dyn_len(&sshc->readdir_buf));
+ curlx_dyn_ptr(&sshc->readdir_buf),
+ curlx_dyn_len(&sshc->readdir_buf));
ssh_string_free_char(sshc->readdir_tmp);
sshc->readdir_tmp = NULL;
if(result) {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
}
else
- state(data, SSH_SFTP_READDIR);
+ myssh_state(data, sshc, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR_DONE:
@@ -1577,7 +1727,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
/* no data to transfer */
Curl_xfer_setup_nop(data);
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_SFTP_DOWNLOAD_INIT:
@@ -1587,7 +1737,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(sshc->sftp_file)
sftp_close(sshc->sftp_file);
- sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path,
+ sshc->sftp_file = sftp_open(sshc->sftp_session, sshp->path,
O_RDONLY, (mode_t)data->set.new_file_perms);
if(!sshc->sftp_file) {
failf(data, "Could not open remote file for reading: %s",
@@ -1597,160 +1747,19 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
break;
}
sftp_file_set_nonblocking(sshc->sftp_file);
- state(data, SSH_SFTP_DOWNLOAD_STAT);
+ myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
break;
case SSH_SFTP_DOWNLOAD_STAT:
- {
- sftp_attributes attrs;
- curl_off_t size;
-
- attrs = sftp_fstat(sshc->sftp_file);
- if(!attrs ||
- !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
- (attrs->size == 0)) {
- /*
- * sftp_fstat did not return an error, so maybe the server
- * just does not support stat()
- * OR the server does not return a file size with a stat()
- * OR file size is 0
- */
- data->req.size = -1;
- data->req.maxdownload = -1;
- Curl_pgrsSetDownloadSize(data, -1);
- size = 0;
- }
- else {
- size = attrs->size;
-
- sftp_attributes_free(attrs);
-
- if(size < 0) {
- failf(data, "Bad file size (%" FMT_OFF_T ")", size);
- return CURLE_BAD_DOWNLOAD_RESUME;
- }
- if(data->state.use_range) {
- curl_off_t from, to;
- char *ptr;
- char *ptr2;
- CURLofft to_t;
- CURLofft from_t;
-
- from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
- if(from_t == CURL_OFFT_FLOW) {
- return CURLE_RANGE_ERROR;
- }
- while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
- ptr++;
- to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
- if(to_t == CURL_OFFT_FLOW) {
- return CURLE_RANGE_ERROR;
- }
- if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
- || (to >= size)) {
- to = size - 1;
- }
- if(from_t) {
- /* from is relative to end of file */
- from = size - to;
- to = size - 1;
- }
- if(from > size) {
- failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
- FMT_OFF_T ")", from, size);
- return CURLE_BAD_DOWNLOAD_RESUME;
- }
- if(from > to) {
- from = to;
- size = 0;
- }
- else {
- if((to - from) == CURL_OFF_T_MAX)
- return CURLE_RANGE_ERROR;
- size = to - from + 1;
- }
-
- rc = sftp_seek64(sshc->sftp_file, from);
- if(rc) {
- MOVE_TO_SFTP_CLOSE_STATE();
- break;
- }
- }
- data->req.size = size;
- data->req.maxdownload = size;
- 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) {
- /* We are supposed to download the last abs(from) bytes */
- if((curl_off_t)size < -data->state.resume_from) {
- failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
- FMT_OFF_T ")", data->state.resume_from, size);
- return CURLE_BAD_DOWNLOAD_RESUME;
- }
- /* download from where? */
- data->state.resume_from += size;
- }
- else {
- if((curl_off_t)size < data->state.resume_from) {
- failf(data, "Offset (%" FMT_OFF_T
- ") was beyond file size (%" FMT_OFF_T ")",
- data->state.resume_from, size);
- return CURLE_BAD_DOWNLOAD_RESUME;
- }
- }
- /* Now store the number of bytes we are expected to download */
- data->req.size = size - data->state.resume_from;
- data->req.maxdownload = size - data->state.resume_from;
- Curl_pgrsSetDownloadSize(data,
- size - data->state.resume_from);
-
- rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
- if(rc) {
- MOVE_TO_SFTP_CLOSE_STATE();
- break;
- }
- }
- }
-
- /* Setup the actual download */
- if(data->req.size == 0) {
- /* no data to transfer */
- Curl_xfer_setup_nop(data);
- infof(data, "File already completely downloaded");
- state(data, SSH_STOP);
+ rc = myssh_state_sftp_download_stat(data, sshc);
break;
- }
- Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
-
- /* not set by Curl_xfer_setup to preserve keepon bits */
- conn->writesockfd = conn->sockfd;
-
- /* we want to use the _receiving_ function even when the socket turns
- out writableable as the underlying libssh recv function will deal
- with both accordingly */
- data->state.select_bits = CURL_CSELECT_IN;
-
- if(result) {
- /* this should never occur; the close state should be entered
- at the time the error occurs */
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = result;
- }
- else {
- sshc->sftp_recv_state = 0;
- state(data, SSH_STOP);
- }
- break;
case SSH_SFTP_CLOSE:
if(sshc->sftp_file) {
sftp_close(sshc->sftp_file);
sshc->sftp_file = NULL;
}
- Curl_safefree(protop->path);
+ Curl_safefree(sshp->path);
DEBUGF(infof(data, "SFTP DONE done"));
@@ -1759,11 +1768,11 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
SSH_SFTP_CLOSE to pass the correct result back */
if(sshc->nextstate != SSH_NO_STATE &&
sshc->nextstate != SSH_SFTP_CLOSE) {
- state(data, sshc->nextstate);
+ myssh_state(data, sshc, sshc->nextstate);
sshc->nextstate = SSH_SFTP_CLOSE;
}
else {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
result = sshc->actualcode;
}
break;
@@ -1772,6 +1781,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
/* 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 */
+ ssh_set_blocking(sshc->ssh_session, 0);
+#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
+ if(sshc->sftp_aio) {
+ sftp_aio_free(sshc->sftp_aio);
+ sshc->sftp_aio = NULL;
+ }
+#endif
if(sshc->sftp_file) {
sftp_close(sshc->sftp_file);
@@ -1784,16 +1800,15 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
SSH_STRING_FREE_CHAR(sshc->homedir);
- data->state.most_recent_ftp_entrypath = NULL;
- state(data, SSH_SESSION_DISCONNECT);
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
break;
case SSH_SCP_TRANS_INIT:
- result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
+ result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
if(result) {
sshc->actualcode = result;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
@@ -1809,13 +1824,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
sshc->scp_session =
- ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path);
- state(data, SSH_SCP_UPLOAD_INIT);
+ ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path);
+ myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT);
}
else {
sshc->scp_session =
- ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path);
- state(data, SSH_SCP_DOWNLOAD_INIT);
+ ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path);
+ myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT);
}
if(!sshc->scp_session) {
@@ -1836,9 +1851,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
break;
}
- rc = ssh_scp_push_file(sshc->scp_session, protop->path,
- data->state.infilesize,
- (int)data->set.new_file_perms);
+ rc = ssh_scp_push_file64(sshc->scp_session, sshp->path,
+ (uint64_t)data->state.infilesize,
+ (int)data->set.new_file_perms);
+
if(rc != SSH_OK) {
err_msg = ssh_get_error(sshc->ssh_session);
failf(data, "%s", err_msg);
@@ -1861,7 +1877,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
with both accordingly */
data->state.select_bits = CURL_CSELECT_OUT;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
@@ -1874,7 +1890,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
break;
}
- state(data, SSH_SCP_DOWNLOAD);
+ myssh_state(data, sshc, SSH_SCP_DOWNLOAD);
FALLTHROUGH();
case SSH_SCP_DOWNLOAD:{
@@ -1901,14 +1917,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
with both accordingly */
data->state.select_bits = CURL_CSELECT_IN;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
case SSH_SCP_DONE:
if(data->state.upload)
- state(data, SSH_SCP_SEND_EOF);
+ myssh_state(data, sshc, SSH_SCP_SEND_EOF);
else
- state(data, SSH_SCP_CHANNEL_FREE);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_SEND_EOF:
@@ -1926,7 +1942,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
}
- state(data, SSH_SCP_CHANNEL_FREE);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_CHANNEL_FREE:
@@ -1938,7 +1954,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
ssh_set_blocking(sshc->ssh_session, 0);
- state(data, SSH_SESSION_DISCONNECT);
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
FALLTHROUGH();
case SSH_SESSION_DISCONNECT:
@@ -1950,6 +1966,15 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
sshc->scp_session = NULL;
}
+ if(sshc->sftp_file) {
+ sftp_close(sshc->sftp_file);
+ sshc->sftp_file = NULL;
+ }
+ if(sshc->sftp_session) {
+ sftp_free(sshc->sftp_session);
+ sshc->sftp_session = NULL;
+ }
+
ssh_disconnect(sshc->ssh_session);
if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) {
/* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
@@ -1959,64 +1984,25 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
SSH_STRING_FREE_CHAR(sshc->homedir);
- data->state.most_recent_ftp_entrypath = NULL;
- state(data, SSH_SESSION_FREE);
+ myssh_state(data, sshc, SSH_SESSION_FREE);
FALLTHROUGH();
case SSH_SESSION_FREE:
- if(sshc->ssh_session) {
- ssh_free(sshc->ssh_session);
- sshc->ssh_session = NULL;
- }
-
- /* worst-case scenario cleanup */
-
- DEBUGASSERT(sshc->ssh_session == NULL);
- DEBUGASSERT(sshc->scp_session == NULL);
-
- if(sshc->readdir_tmp) {
- ssh_string_free_char(sshc->readdir_tmp);
- sshc->readdir_tmp = NULL;
- }
-
- if(sshc->quote_attrs)
- sftp_attributes_free(sshc->quote_attrs);
-
- if(sshc->readdir_attrs)
- sftp_attributes_free(sshc->readdir_attrs);
-
- if(sshc->readdir_link_attrs)
- sftp_attributes_free(sshc->readdir_link_attrs);
-
- if(sshc->privkey)
- ssh_key_free(sshc->privkey);
- if(sshc->pubkey)
- ssh_key_free(sshc->pubkey);
-
- Curl_safefree(sshc->rsa_pub);
- Curl_safefree(sshc->rsa);
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
- Curl_dyn_free(&sshc->readdir_buf);
- Curl_safefree(sshc->readdir_linkPath);
- SSH_STRING_FREE_CHAR(sshc->homedir);
-
+ sshc_cleanup(sshc);
/* the code we are about to return */
result = sshc->actualcode;
-
memset(sshc, 0, sizeof(struct ssh_conn));
-
connclose(conn, "SSH session free");
sshc->state = SSH_SESSION_FREE; /* current */
sshc->nextstate = SSH_NO_STATE;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_QUIT:
default:
/* internal error */
sshc->nextstate = SSH_NO_STATE;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
@@ -2056,10 +2042,10 @@ static int myssh_getsock(struct Curl_easy *data,
return bitmap;
}
-static void myssh_block2waitfor(struct connectdata *conn, bool block)
+static void myssh_block2waitfor(struct connectdata *conn,
+ struct ssh_conn *sshc,
+ bool block)
{
- struct ssh_conn *sshc = &conn->proto.sshc;
-
/* If it did not block, or nothing was returned by ssh_get_poll_flags
* have the original set */
conn->waitfor = sshc->orig_waitfor;
@@ -2080,30 +2066,35 @@ static CURLcode myssh_multi_statemach(struct Curl_easy *data,
bool *done)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
bool block; /* we store the status and use that to provide a ssh_getsock()
implementation */
- CURLcode result = myssh_statemach_act(data, &block);
+ CURLcode result;
+ if(!sshc || !sshp)
+ return CURLE_FAILED_INIT;
+ result = myssh_statemach_act(data, sshc, sshp, &block);
*done = (sshc->state == SSH_STOP);
- myssh_block2waitfor(conn, block);
+ myssh_block2waitfor(conn, sshc, block);
return result;
}
static CURLcode myssh_block_statemach(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
bool disconnect)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
while((sshc->state != SSH_STOP) && !result) {
bool block;
timediff_t left = 1000;
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
- result = myssh_statemach_act(data, &block);
+ result = myssh_statemach_act(data, sshc, sshp, &block);
if(result)
break;
@@ -2134,19 +2125,46 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data,
return result;
}
+static void myssh_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct SSHPROTO *sshp = entry;
+ (void)key;
+ (void)klen;
+ Curl_safefree(sshp->path);
+ free(sshp);
+}
+
+static void myssh_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct ssh_conn *sshc = entry;
+ (void)key;
+ (void)klen;
+ sshc_cleanup(sshc);
+ free(sshc);
+}
+
/*
* SSH setup connection
*/
static CURLcode myssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
- struct SSHPROTO *ssh;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct SSHPROTO *sshp;
+ struct ssh_conn *sshc;
+
+ sshc = calloc(1, sizeof(*sshc));
+ if(!sshc)
+ return CURLE_OUT_OF_MEMORY;
+
+ curlx_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2);
+ sshc->initialised = TRUE;
+ if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
- data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
- if(!ssh)
+ sshp = calloc(1, sizeof(*sshp));
+ if(!sshp ||
+ Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor))
return CURLE_OUT_OF_MEMORY;
- Curl_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2);
return CURLE_OK;
}
@@ -2160,15 +2178,15 @@ static Curl_send scp_send, sftp_send;
*/
static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
{
- struct ssh_conn *ssh;
CURLcode result;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *ssh = Curl_meta_get(data, CURL_META_SSH_EASY);
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc;
- /* initialize per-handle data if not already */
- if(!data->req.p.ssh)
- myssh_setup_connection(data, conn);
+ if(!sshc || !ssh)
+ return CURLE_FAILED_INIT;
/* We default to persistent connections. We set this already in this connect
function to make the reuse checks properly be able to check this bit. */
@@ -2183,27 +2201,32 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
conn->send[FIRSTSOCKET] = sftp_send;
}
- ssh = &conn->proto.sshc;
-
- ssh->ssh_session = ssh_new();
- if(!ssh->ssh_session) {
+ sshc->ssh_session = ssh_new();
+ if(!sshc->ssh_session) {
failf(data, "Failure initialising ssh session");
return CURLE_FAILED_INIT;
}
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
+ if(conn->bits.ipv6_ip) {
+ char ipv6[MAX_IPADR_LEN];
+ msnprintf(ipv6, sizeof(ipv6), "[%s]", conn->host.name);
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, ipv6);
+ }
+ else
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
+
if(rc != SSH_OK) {
failf(data, "Could not set remote host");
return CURLE_FAILED_INIT;
}
- rc = ssh_options_parse_config(ssh->ssh_session, NULL);
+ rc = ssh_options_parse_config(sshc->ssh_session, NULL);
if(rc != SSH_OK) {
infof(data, "Could not parse SSH configuration files");
/* ignore */
}
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock);
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_FD, &sock);
if(rc != SSH_OK) {
failf(data, "Could not set socket");
return CURLE_FAILED_INIT;
@@ -2211,7 +2234,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
if(conn->user && conn->user[0] != '\0') {
infof(data, "User: %s", conn->user);
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user);
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, conn->user);
if(rc != SSH_OK) {
failf(data, "Could not set user");
return CURLE_FAILED_INIT;
@@ -2220,7 +2243,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]);
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
data->set.str[STRING_SSH_KNOWNHOSTS]);
if(rc != SSH_OK) {
failf(data, "Could not set known hosts file path");
@@ -2229,7 +2252,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
}
if(conn->remote_port) {
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT,
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_PORT,
&conn->remote_port);
if(rc != SSH_OK) {
failf(data, "Could not set remote port");
@@ -2238,7 +2261,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
}
if(data->set.ssh_compression) {
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION,
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_COMPRESSION,
"zlib,zlib@openssh.com,none");
if(rc != SSH_OK) {
failf(data, "Could not set compression");
@@ -2246,12 +2269,12 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
}
}
- ssh->privkey = NULL;
- ssh->pubkey = NULL;
+ sshc->privkey = NULL;
+ sshc->pubkey = NULL;
if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
- &ssh->pubkey);
+ &sshc->pubkey);
if(rc != SSH_OK) {
failf(data, "Could not load public key file");
return CURLE_FAILED_INIT;
@@ -2261,7 +2284,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
/* we do not verify here, we do it at the state machine,
* after connection */
- state(data, SSH_INIT);
+ myssh_state(data, sshc, SSH_INIT);
result = myssh_multi_statemach(data, done);
@@ -2295,13 +2318,16 @@ CURLcode scp_perform(struct Curl_easy *data,
bool *connected, bool *dophase_done)
{
CURLcode result = CURLE_OK;
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
DEBUGF(infof(data, "DO phase starts"));
*dophase_done = FALSE; /* not done yet */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* start the first command in the DO phase */
- state(data, SSH_SCP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SCP_TRANS_INIT);
result = myssh_multi_statemach(data, dophase_done);
@@ -2319,9 +2345,11 @@ static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
CURLcode result;
bool connected = FALSE;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
*done = FALSE; /* default to false */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
data->req.size = -1; /* make sure this is unknown at this point */
@@ -2342,6 +2370,62 @@ static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
return result;
}
+static void sshc_cleanup(struct ssh_conn *sshc)
+{
+ if(sshc->initialised) {
+ if(sshc->sftp_file) {
+ sftp_close(sshc->sftp_file);
+ sshc->sftp_file = NULL;
+ }
+ if(sshc->sftp_session) {
+ sftp_free(sshc->sftp_session);
+ sshc->sftp_session = NULL;
+ }
+ if(sshc->ssh_session) {
+ ssh_free(sshc->ssh_session);
+ sshc->ssh_session = NULL;
+ }
+
+ /* worst-case scenario cleanup */
+ DEBUGASSERT(sshc->ssh_session == NULL);
+ DEBUGASSERT(sshc->scp_session == NULL);
+
+ if(sshc->readdir_tmp) {
+ ssh_string_free_char(sshc->readdir_tmp);
+ sshc->readdir_tmp = NULL;
+ }
+ if(sshc->quote_attrs) {
+ sftp_attributes_free(sshc->quote_attrs);
+ sshc->quote_attrs = NULL;
+ }
+ if(sshc->readdir_attrs) {
+ sftp_attributes_free(sshc->readdir_attrs);
+ sshc->readdir_attrs = NULL;
+ }
+ if(sshc->readdir_link_attrs) {
+ sftp_attributes_free(sshc->readdir_link_attrs);
+ sshc->readdir_link_attrs = NULL;
+ }
+ if(sshc->privkey) {
+ ssh_key_free(sshc->privkey);
+ sshc->privkey = NULL;
+ }
+ if(sshc->pubkey) {
+ ssh_key_free(sshc->pubkey);
+ sshc->pubkey = NULL;
+ }
+
+ Curl_safefree(sshc->rsa_pub);
+ Curl_safefree(sshc->rsa);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ curlx_dyn_free(&sshc->readdir_buf);
+ Curl_safefree(sshc->readdir_linkPath);
+ SSH_STRING_FREE_CHAR(sshc->homedir);
+ sshc->initialised = FALSE;
+ }
+}
+
/* 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 */
@@ -2350,15 +2434,16 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
bool dead_connection)
{
CURLcode result = CURLE_OK;
- struct ssh_conn *ssh = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
(void) dead_connection;
- if(ssh->ssh_session) {
+ if(sshc && sshc->ssh_session && sshp) {
/* only if there is a session still around to use! */
- state(data, SSH_SESSION_DISCONNECT);
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
- result = myssh_block_statemach(data, TRUE);
+ result = myssh_block_statemach(data, sshc, sshp, TRUE);
}
return result;
@@ -2366,20 +2451,20 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
/* generic done function for both SCP and SFTP called from their specific
done functions */
-static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
+static CURLcode myssh_done(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ CURLcode status)
{
CURLcode result = CURLE_OK;
- struct SSHPROTO *protop = data->req.p.ssh;
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
- if(!status) {
+ if(!status && sshp) {
/* run the state-machine */
- result = myssh_block_statemach(data, FALSE);
+ result = myssh_block_statemach(data, sshc, sshp, FALSE);
}
else
result = status;
- if(protop)
- Curl_safefree(protop->path);
if(Curl_pgrsDone(data))
return CURLE_ABORTED_BY_CALLBACK;
@@ -2391,13 +2476,15 @@ static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
(void) premature; /* not used */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
if(!status)
- state(data, SSH_SCP_DONE);
-
- return myssh_done(data, status);
+ myssh_state(data, sshc, SSH_SCP_DONE);
+ return myssh_done(data, sshc, status);
}
static ssize_t scp_send(struct Curl_easy *data, int sockindex,
@@ -2405,17 +2492,22 @@ static ssize_t scp_send(struct Curl_easy *data, int sockindex,
{
int rc;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void) sockindex; /* we only support SCP on the fixed known primary socket */
- (void) err;
(void)eos;
- rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len);
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
+
+ rc = ssh_scp_write(sshc->scp_session, mem, len);
#if 0
/* The following code is misleading, mostly added as wishful thinking
* that libssh at some point will implement non-blocking ssh_scp_write/read.
* Currently rc can only be number of bytes read or SSH_ERROR. */
- myssh_block2waitfor(conn, (rc == SSH_AGAIN));
+ myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN));
if(rc == SSH_AGAIN) {
*err = CURLE_AGAIN;
@@ -2436,18 +2528,22 @@ static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
{
ssize_t nread;
struct connectdata *conn = data->conn;
- (void) err;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void) sockindex; /* we only support SCP on the fixed known primary socket */
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
/* libssh returns int */
- nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len);
+ nread = ssh_scp_read(sshc->scp_session, mem, len);
#if 0
/* The following code is misleading, mostly added as wishful thinking
* that libssh at some point will implement non-blocking ssh_scp_write/read.
* Currently rc can only be SSH_OK or SSH_ERROR. */
- myssh_block2waitfor(conn, (nread == SSH_AGAIN));
+ myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN));
if(nread == SSH_AGAIN) {
*err = CURLE_AGAIN;
nread = -1;
@@ -2475,14 +2571,17 @@ CURLcode sftp_perform(struct Curl_easy *data,
bool *connected,
bool *dophase_done)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
DEBUGF(infof(data, "DO phase starts"));
*dophase_done = FALSE; /* not done yet */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* start the first command in the DO phase */
- state(data, SSH_SFTP_QUOTE_INIT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_INIT);
/* run the state-machine */
result = myssh_multi_statemach(data, dophase_done);
@@ -2514,38 +2613,40 @@ static CURLcode sftp_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
CURLcode result = CURLE_OK;
(void) dead_connection;
DEBUGF(infof(data, "SSH DISCONNECT starts now"));
- if(conn->proto.sshc.ssh_session) {
+ if(sshc && sshc->ssh_session && sshp) {
/* only if there is a session still around to use! */
- state(data, SSH_SFTP_SHUTDOWN);
- result = myssh_block_statemach(data, TRUE);
+ myssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
+ result = myssh_block_statemach(data, sshc, sshp, TRUE);
}
DEBUGF(infof(data, "SSH DISCONNECT is done"));
-
return result;
-
}
static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ if(!sshc)
+ return CURLE_FAILED_INIT;
if(!status) {
/* 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(!premature && data->set.postquote && !conn->bits.retry)
sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
}
- return myssh_done(data, status);
+ return myssh_done(data, sshc, status);
}
/* return number of sent bytes */
@@ -2555,18 +2656,55 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
{
ssize_t nwrite;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex;
(void)eos;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
/* limit the writes to the maximum specified in Section 3 of
* https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
*/
if(len > 32768)
len = 32768;
+#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
+ switch(sshc->sftp_send_state) {
+ case 0:
+ sftp_file_set_nonblocking(sshc->sftp_file);
+ if(sftp_aio_begin_write(sshc->sftp_file, mem, len,
+ &sshc->sftp_aio) == SSH_ERROR) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ sshc->sftp_send_state = 1;
+ FALLTHROUGH();
+ case 1:
+ nwrite = sftp_aio_wait_write(&sshc->sftp_aio);
+ myssh_block2waitfor(conn, sshc, (nwrite == SSH_AGAIN) ? TRUE : FALSE);
+ if(nwrite == SSH_AGAIN) {
+ *err = CURLE_AGAIN;
+ return 0;
+ }
+ else if(nwrite < 0) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ if(sshc->sftp_aio) {
+ sftp_aio_free(sshc->sftp_aio);
+ sshc->sftp_aio = NULL;
+ }
+ sshc->sftp_send_state = 0;
+ return nwrite;
+ default:
+ /* we never reach here */
+ return -1;
+ }
+#else
+ nwrite = sftp_write(sshc->sftp_file, mem, len);
- nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len);
-
- myssh_block2waitfor(conn, FALSE);
+ myssh_block2waitfor(conn, sshc, FALSE);
#if 0 /* not returned by libssh on write */
if(nwrite == SSH_AGAIN) {
@@ -2581,6 +2719,7 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
}
return nwrite;
+#endif
}
/*
@@ -2592,29 +2731,31 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
{
ssize_t nread;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex;
DEBUGASSERT(len < CURL_MAX_READ_SIZE);
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
- switch(conn->proto.sshc.sftp_recv_state) {
+ switch(sshc->sftp_recv_state) {
case 0:
- conn->proto.sshc.sftp_file_index =
- sftp_async_read_begin(conn->proto.sshc.sftp_file,
- (uint32_t)len);
- if(conn->proto.sshc.sftp_file_index < 0) {
+ sshc->sftp_file_index =
+ sftp_async_read_begin(sshc->sftp_file, (uint32_t)len);
+ if(sshc->sftp_file_index < 0) {
*err = CURLE_RECV_ERROR;
return -1;
}
FALLTHROUGH();
case 1:
- conn->proto.sshc.sftp_recv_state = 1;
-
- nread = sftp_async_read(conn->proto.sshc.sftp_file,
- mem, (uint32_t)len,
- (uint32_t)conn->proto.sshc.sftp_file_index);
+ sshc->sftp_recv_state = 1;
+ nread = sftp_async_read(sshc->sftp_file, mem, (uint32_t)len,
+ (uint32_t)sshc->sftp_file_index);
- myssh_block2waitfor(conn, (nread == SSH_AGAIN));
+ myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN));
if(nread == SSH_AGAIN) {
*err = CURLE_AGAIN;
@@ -2625,7 +2766,7 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
return -1;
}
- conn->proto.sshc.sftp_recv_state = 0;
+ sshc->sftp_recv_state = 0;
return nread;
default:
@@ -2634,12 +2775,11 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
}
}
-static void sftp_quote(struct Curl_easy *data)
+static void sftp_quote(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
{
const char *cp;
- struct connectdata *conn = data->conn;
- struct SSHPROTO *protop = data->req.p.ssh;
- struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result;
/*
@@ -2660,15 +2800,14 @@ static void sftp_quote(struct Curl_easy *data)
if(strcasecompare("pwd", cmd)) {
/* output debug output if that is requested */
- char *tmp = aprintf("257 \"%s\" is current directory.\n",
- protop->path);
+ char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path);
if(!tmp) {
sshc->actualcode = CURLE_OUT_OF_MEMORY;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
return;
}
- Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4);
+ Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4);
Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
/* this sends an FTP-like "header" to the header callback so that the
@@ -2677,12 +2816,12 @@ static void sftp_quote(struct Curl_easy *data)
result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
free(tmp);
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
}
else
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
return;
}
@@ -2693,7 +2832,7 @@ static void sftp_quote(struct Curl_easy *data)
cp = strchr(cmd, ' ');
if(!cp) {
failf(data, "Syntax error in SFTP command. Supply parameter(s)");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
@@ -2709,7 +2848,7 @@ static void sftp_quote(struct Curl_easy *data)
failf(data, "Out of memory");
else
failf(data, "Syntax error: Bad first parameter");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
return;
@@ -2721,11 +2860,11 @@ static void sftp_quote(struct Curl_easy *data)
* OpenSSH's sftp program and call the appropriate libssh
* functions.
*/
- if(strncasecompare(cmd, "chgrp ", 6) ||
- strncasecompare(cmd, "chmod ", 6) ||
- strncasecompare(cmd, "chown ", 6) ||
- strncasecompare(cmd, "atime ", 6) ||
- strncasecompare(cmd, "mtime ", 6)) {
+ if(!strncmp(cmd, "chgrp ", 6) ||
+ !strncmp(cmd, "chmod ", 6) ||
+ !strncmp(cmd, "chown ", 6) ||
+ !strncmp(cmd, "atime ", 6) ||
+ !strncmp(cmd, "mtime ", 6)) {
/* attribute change */
/* sshc->quote_path1 contains the mode to set */
@@ -2738,17 +2877,17 @@ static void sftp_quote(struct Curl_easy *data)
failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: "
"Bad second parameter");
Curl_safefree(sshc->quote_path1);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
return;
}
sshc->quote_attrs = NULL;
- state(data, SSH_SFTP_QUOTE_STAT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_STAT);
return;
}
- if(strncasecompare(cmd, "ln ", 3) ||
- strncasecompare(cmd, "symlink ", 8)) {
+ if(!strncmp(cmd, "ln ", 3) ||
+ !strncmp(cmd, "symlink ", 8)) {
/* symbolic linking */
/* sshc->quote_path1 is the source */
/* get the destination */
@@ -2759,20 +2898,20 @@ static void sftp_quote(struct Curl_easy *data)
else
failf(data, "Syntax error in ln/symlink: Bad second parameter");
Curl_safefree(sshc->quote_path1);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
return;
}
- state(data, SSH_SFTP_QUOTE_SYMLINK);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK);
return;
}
- else if(strncasecompare(cmd, "mkdir ", 6)) {
+ else if(!strncmp(cmd, "mkdir ", 6)) {
/* create dir */
- state(data, SSH_SFTP_QUOTE_MKDIR);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR);
return;
}
- else if(strncasecompare(cmd, "rename ", 7)) {
+ else if(!strncmp(cmd, "rename ", 7)) {
/* rename file */
/* first param is the source path */
/* second param is the dest. path */
@@ -2783,26 +2922,26 @@ static void sftp_quote(struct Curl_easy *data)
else
failf(data, "Syntax error in rename: Bad second parameter");
Curl_safefree(sshc->quote_path1);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
return;
}
- state(data, SSH_SFTP_QUOTE_RENAME);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME);
return;
}
- else if(strncasecompare(cmd, "rmdir ", 6)) {
+ else if(!strncmp(cmd, "rmdir ", 6)) {
/* delete dir */
- state(data, SSH_SFTP_QUOTE_RMDIR);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR);
return;
}
- else if(strncasecompare(cmd, "rm ", 3)) {
- state(data, SSH_SFTP_QUOTE_UNLINK);
+ else if(!strncmp(cmd, "rm ", 3)) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK);
return;
}
#ifdef HAS_STATVFS_SUPPORT
- else if(strncasecompare(cmd, "statvfs ", 8)) {
- state(data, SSH_SFTP_QUOTE_STATVFS);
+ else if(!strncmp(cmd, "statvfs ", 8)) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS);
return;
}
#endif
@@ -2810,15 +2949,14 @@ static void sftp_quote(struct Curl_easy *data)
failf(data, "Unknown SFTP command");
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
}
-static void sftp_quote_stat(struct Curl_easy *data)
+static void sftp_quote_stat(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
char *cmd = sshc->quote_item->data;
sshc->acceptfail = FALSE;
@@ -2845,59 +2983,63 @@ static void sftp_quote_stat(struct Curl_easy *data)
Curl_safefree(sshc->quote_path2);
failf(data, "Attempt to get SFTP stats failed: %d",
sftp_get_error(sshc->sftp_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
/* Now set the new attributes... */
- if(strncasecompare(cmd, "chgrp", 5)) {
- sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
+ if(!strncmp(cmd, "chgrp", 5)) {
+ const char *p = sshc->quote_path1;
+ curl_off_t gid;
+ (void)curlx_str_number(&p, &gid, UINT_MAX);
+ sshc->quote_attrs->gid = (uint32_t)gid;
if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
- !sshc->acceptfail) {
+ !sshc->acceptfail) {
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
failf(data, "Syntax error: chgrp gid not a number");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
}
- else if(strncasecompare(cmd, "chmod", 5)) {
- mode_t perms;
- perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8);
- /* permissions are octal */
- if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) {
+ else if(!strncmp(cmd, "chmod", 5)) {
+ curl_off_t perms;
+ const char *p = sshc->quote_path1;
+ if(curlx_str_octal(&p, &perms, 07777)) {
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
failf(data, "Syntax error: chmod permissions not a number");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
- sshc->quote_attrs->permissions = perms;
+ sshc->quote_attrs->permissions = (mode_t)perms;
sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
}
- else if(strncasecompare(cmd, "chown", 5)) {
- sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
+ else if(!strncmp(cmd, "chown", 5)) {
+ const char *p = sshc->quote_path1;
+ curl_off_t uid;
+ (void)curlx_str_number(&p, &uid, UINT_MAX);
if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
- !sshc->acceptfail) {
+ !sshc->acceptfail) {
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
failf(data, "Syntax error: chown uid not a number");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
}
- else if(strncasecompare(cmd, "atime", 5) ||
- strncasecompare(cmd, "mtime", 5)) {
+ else if(!strncmp(cmd, "atime", 5) ||
+ !strncmp(cmd, "mtime", 5)) {
time_t date = Curl_getdate_capped(sshc->quote_path1);
bool fail = FALSE;
if(date == -1) {
@@ -2913,12 +3055,12 @@ static void sftp_quote_stat(struct Curl_easy *data)
if(fail) {
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
- if(strncasecompare(cmd, "atime", 5))
+ if(!strncmp(cmd, "atime", 5))
sshc->quote_attrs->atime = (uint32_t)date;
else /* mtime */
sshc->quote_attrs->mtime = (uint32_t)date;
@@ -2927,7 +3069,7 @@ static void sftp_quote_stat(struct Curl_easy *data)
}
/* Now send the completed structure... */
- state(data, SSH_SFTP_QUOTE_SETSTAT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_SETSTAT);
return;
}
diff --git a/libs/libcurl/src/vssh/libssh2.c b/libs/libcurl/src/vssh/libssh2.c
index ee3847324b..f0081ecb0b 100644
--- a/libs/libcurl/src/vssh/libssh2.c
+++ b/libs/libcurl/src/vssh/libssh2.c
@@ -24,7 +24,7 @@
/* #define CURL_LIBSSH2_DEBUG */
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_LIBSSH2
@@ -49,55 +49,45 @@
#endif
#include <curl/curl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "progress.h"
-#include "transfer.h"
-#include "escape.h"
-#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../hostip.h"
+#include "../progress.h"
+#include "../transfer.h"
+#include "../escape.h"
+#include "../http.h" /* for HTTP proxy tunnel stuff */
#include "ssh.h"
-#include "url.h"
-#include "speedcheck.h"
-#include "getinfo.h"
-#include "strdup.h"
-#include "strcase.h"
-#include "vtls/vtls.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "inet_ntop.h"
-#include "parsedate.h" /* for the week day and month names */
-#include "sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "strtoofft.h"
-#include "multiif.h"
-#include "select.h"
-#include "warnless.h"
+#include "../url.h"
+#include "../speedcheck.h"
+#include "../getinfo.h"
+#include "../strdup.h"
+#include "../strcase.h"
+#include "../vtls/vtls.h"
+#include "../cfilters.h"
+#include "../connect.h"
+#include "../inet_ntop.h"
+#include "../parsedate.h" /* for the week day and month names */
+#include "../sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "../multiif.h"
+#include "../select.h"
+#include "../curlx/warnless.h"
#include "curl_path.h"
-
-#include <curl_base64.h> /* for base64 encoding/decoding */
-#include <curl_sha256.h>
-
+#include "../curlx/strparse.h"
+#include "../curlx/base64.h" /* for base64 encoding/decoding */
+#include "../curl_sha256.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#if LIBSSH2_VERSION_NUM >= 0x010206
-/* libssh2_sftp_statvfs and friends were added in 1.2.6 */
-#define HAS_STATVFS_SUPPORT 1
-#endif
-
-#define sftp_libssh2_realpath(s,p,t,m) \
- libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \
- (t), (m), LIBSSH2_SFTP_REALPATH)
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* Local functions: */
static const char *sftp_libssh2_strerror(unsigned long err);
static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
static LIBSSH2_FREE_FUNC(my_libssh2_free);
-static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data);
+static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data,
+ struct ssh_conn *sshc);
static CURLcode ssh_connect(struct Curl_easy *data, bool *done);
static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done);
static CURLcode ssh_do(struct Curl_easy *data, bool *done);
@@ -116,7 +106,8 @@ static int ssh_getsock(struct Curl_easy *data, struct connectdata *conn,
static CURLcode ssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
static void ssh_attach(struct Curl_easy *data, struct connectdata *conn);
-
+static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data,
+ bool block);
/*
* SCP protocol handler.
*/
@@ -139,6 +130,7 @@ const struct Curl_handler Curl_handler_scp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ssh_attach, /* attach */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SCP, /* protocol */
CURLPROTO_SCP, /* family */
@@ -169,6 +161,7 @@ const struct Curl_handler Curl_handler_sftp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ssh_attach, /* attach */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */
@@ -306,10 +299,10 @@ static LIBSSH2_FREE_FUNC(my_libssh2_free)
* SSH State machine related code
*/
/* This is the ONLY way to change SSH state! */
-static void state(struct Curl_easy *data, sshstate nowstate)
+static void myssh_state(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ sshstate nowstate)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char * const names[] = {
@@ -376,19 +369,17 @@ static void state(struct Curl_easy *data, sshstate nowstate)
};
/* a precaution to make sure the lists are in sync */
- DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
+ DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST);
if(sshc->state != nowstate) {
infof(data, "SFTP %p state change from %s to %s",
(void *)sshc, names[sshc->state], names[nowstate]);
}
#endif
-
+ (void)data;
sshc->state = nowstate;
}
-
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
static int sshkeycallback(CURL *easy,
const struct curl_khkey *knownkey, /* known */
const struct curl_khkey *foundkey, /* found */
@@ -403,37 +394,7 @@ static int sshkeycallback(CURL *easy,
/* we only allow perfect matches, and we reject everything else */
return (match != CURLKHMATCH_OK) ? CURLKHSTAT_REJECT : CURLKHSTAT_FINE;
}
-#endif
-
-/*
- * Earlier libssh2 versions did not have the ability to seek to 64-bit
- * positions with 32-bit size_t.
- */
-#ifdef HAVE_LIBSSH2_SFTP_SEEK64
-#define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y)
-#else
-#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y)
-#endif
-/*
- * Earlier libssh2 versions did not do SCP properly beyond 32-bit sizes on
- * 32-bit architectures so we check of the necessary function is present.
- */
-#ifndef HAVE_LIBSSH2_SCP_SEND64
-#define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0)
-#else
-#define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c), \
- (libssh2_int64_t)d, 0, 0)
-#endif
-
-/*
- * libssh2 1.2.8 fixed the problem with 32-bit ints used for sockets on win64.
- */
-#ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE
-#define session_startup(x,y) libssh2_session_handshake(x, y)
-#else
-#define session_startup(x,y) libssh2_session_startup(x, (int)y)
-#endif
static enum curl_khtype convert_ssh2_keytype(int sshkeytype)
{
enum curl_khtype keytype = CURLKHTYPE_UNKNOWN;
@@ -468,18 +429,17 @@ static enum curl_khtype convert_ssh2_keytype(int sshkeytype)
return keytype;
}
-static CURLcode ssh_knownhost(struct Curl_easy *data)
+static CURLcode ssh_knownhost(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
int sshkeytype = 0;
size_t keylen = 0;
int rc = 0;
CURLcode result = CURLE_OK;
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
/* we are asked to verify the host against a file */
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
struct libssh2_knownhost *host = NULL;
const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
&keylen, &sshkeytype);
@@ -535,7 +495,6 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
/* no check means failure! */
rc = CURLKHSTAT_REJECT;
else {
-#ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP
keycheck = libssh2_knownhost_checkp(sshc->kh,
conn->host.name,
(conn->remote_port != PORT_SSH) ?
@@ -545,15 +504,6 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
LIBSSH2_KNOWNHOST_KEYENC_RAW|
keybit,
&host);
-#else
- keycheck = libssh2_knownhost_check(sshc->kh,
- conn->host.name,
- remotekey, keylen,
- LIBSSH2_KNOWNHOST_TYPE_PLAIN|
- LIBSSH2_KNOWNHOST_KEYENC_RAW|
- keybit,
- &host);
-#endif
infof(data, "SSH host check: %d, key: %s", keycheck,
(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) ?
@@ -594,11 +544,11 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
switch(rc) {
default: /* unknown return codes will equal reject */
case CURLKHSTAT_REJECT:
- state(data, SSH_SESSION_FREE);
+ myssh_state(data, sshc, SSH_SESSION_FREE);
FALLTHROUGH();
case CURLKHSTAT_DEFER:
/* DEFER means bail out but keep the SSH_HOSTKEY state */
- result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ result = CURLE_PEER_FAILED_VERIFICATION;
break;
case CURLKHSTAT_FINE_REPLACE:
/* remove old host+key that does not match */
@@ -637,23 +587,19 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
break;
}
}
-#else /* HAVE_LIBSSH2_KNOWNHOST_API */
- (void)data;
-#endif
return result;
}
-static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
+static CURLcode ssh_check_fingerprint(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256];
infof(data, "SSH MD5 public key: %s",
- pubkey_md5 != NULL ? pubkey_md5 : "NULL");
+ pubkey_md5 != NULL ? pubkey_md5 : "NULL");
infof(data, "SSH SHA256 public key: %s",
- pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
+ pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
if(pubkey_sha256) {
const char *fingerprint = NULL;
@@ -682,25 +628,22 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
failf(data,
"Denied establishing ssh session: sha256 fingerprint "
"not available");
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
/* The length of fingerprint is 32 bytes for SHA256.
* See libssh2_hostkey_hash documentation. */
- if(Curl_base64_encode(fingerprint, 32, &fingerprint_b64,
- &fingerprint_b64_len) != CURLE_OK) {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ if(curlx_base64_encode(fingerprint, 32, &fingerprint_b64,
+ &fingerprint_b64_len) != CURLE_OK) {
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
if(!fingerprint_b64) {
failf(data, "sha256 fingerprint could not be encoded");
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64);
@@ -725,9 +668,8 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
"Denied establishing ssh session: mismatch sha256 fingerprint. "
"Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256);
free(fingerprint_b64);
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
free(fingerprint_b64);
@@ -737,7 +679,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
if(pubkey_md5) {
char md5buffer[33];
- const char *fingerprint = NULL;
+ const char *fingerprint;
fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
LIBSSH2_HOSTKEY_HASH_MD5);
@@ -765,9 +707,8 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
"Denied establishing ssh session: md5 fingerprint "
"not available");
}
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
infof(data, "MD5 checksum match");
}
@@ -787,20 +728,18 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
(int)keytype, remotekey, keylen);
Curl_set_in_callback(data, FALSE);
if(rc!= CURLKHMATCH_OK) {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
}
else {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
return CURLE_OK;
}
else {
- return ssh_knownhost(data);
+ return ssh_knownhost(data, sshc);
}
}
else {
@@ -813,12 +752,11 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
* ssh_force_knownhost_key_type() will check the known hosts file and try to
* force a specific public key type from the server if an entry is found.
*/
-static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
+static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
CURLcode result = CURLE_OK;
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
-
#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
static const char * const hostkey_method_ssh_ed25519
= "ssh-ed25519";
@@ -844,14 +782,15 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
const char *hostkey_method = NULL;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
struct libssh2_knownhost* store = NULL;
const char *kh_name_end = NULL;
size_t kh_name_size = 0;
int port = 0;
bool found = FALSE;
- if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
+ if(sshc->kh &&
+ !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] &&
+ !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) {
/* lets try to find our host in the known hosts file */
while(!libssh2_knownhost_get(sshc->kh, &store, store)) {
/* For non-standard ports, the name will be enclosed in */
@@ -914,12 +853,10 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
break;
#endif
case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
-#ifdef HAVE_LIBSSH2_VERSION
if(libssh2_version(0x010900))
/* since 1.9.0 libssh2_session_method_pref() works as expected */
hostkey_method = hostkey_method_ssh_rsa_all;
else
-#endif
/* old libssh2 which cannot correctly remove unsupported methods due
* to bug in src/kex.c or does not support the new methods anyways.
*/
@@ -954,8 +891,6 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
}
}
-#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
-
return result;
}
@@ -990,7 +925,7 @@ static CURLcode sftp_quote(struct Curl_easy *data,
char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
- Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4);
+ Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4);
Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
/* this sends an FTP-like "header" to the header callback so that the
@@ -999,7 +934,7 @@ static CURLcode sftp_quote(struct Curl_easy *data,
result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
free(tmp);
if(!result)
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
return result;
}
@@ -1029,11 +964,11 @@ static CURLcode sftp_quote(struct Curl_easy *data,
* Instead, we scan for commands used by OpenSSH's sftp program and call the
* appropriate libssh2 functions.
*/
- if(strncasecompare(cmd, "chgrp ", 6) ||
- strncasecompare(cmd, "chmod ", 6) ||
- strncasecompare(cmd, "chown ", 6) ||
- strncasecompare(cmd, "atime ", 6) ||
- strncasecompare(cmd, "mtime ", 6)) {
+ if(!strncmp(cmd, "chgrp ", 6) ||
+ !strncmp(cmd, "chmod ", 6) ||
+ !strncmp(cmd, "chown ", 6) ||
+ !strncmp(cmd, "atime ", 6) ||
+ !strncmp(cmd, "mtime ", 6)) {
/* attribute change */
/* sshc->quote_path1 contains the mode to set */
@@ -1046,11 +981,11 @@ static CURLcode sftp_quote(struct Curl_easy *data,
return result;
}
memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
- state(data, SSH_SFTP_QUOTE_STAT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_STAT);
return result;
}
- if(strncasecompare(cmd, "ln ", 3) ||
- strncasecompare(cmd, "symlink ", 8)) {
+ if(!strncmp(cmd, "ln ", 3) ||
+ !strncmp(cmd, "symlink ", 8)) {
/* symbolic linking */
/* sshc->quote_path1 is the source */
/* get the destination */
@@ -1061,15 +996,15 @@ static CURLcode sftp_quote(struct Curl_easy *data,
Curl_safefree(sshc->quote_path1);
return result;
}
- state(data, SSH_SFTP_QUOTE_SYMLINK);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK);
return result;
}
- else if(strncasecompare(cmd, "mkdir ", 6)) {
+ else if(!strncmp(cmd, "mkdir ", 6)) {
/* create dir */
- state(data, SSH_SFTP_QUOTE_MKDIR);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR);
return result;
}
- else if(strncasecompare(cmd, "rename ", 7)) {
+ else if(!strncmp(cmd, "rename ", 7)) {
/* rename file */
/* first param is the source path */
/* second param is the dest. path */
@@ -1080,24 +1015,22 @@ static CURLcode sftp_quote(struct Curl_easy *data,
Curl_safefree(sshc->quote_path1);
return result;
}
- state(data, SSH_SFTP_QUOTE_RENAME);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME);
return result;
}
- else if(strncasecompare(cmd, "rmdir ", 6)) {
+ else if(!strncmp(cmd, "rmdir ", 6)) {
/* delete dir */
- state(data, SSH_SFTP_QUOTE_RMDIR);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR);
return result;
}
- else if(strncasecompare(cmd, "rm ", 3)) {
- state(data, SSH_SFTP_QUOTE_UNLINK);
+ else if(!strncmp(cmd, "rm ", 3)) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK);
return result;
}
-#ifdef HAS_STATVFS_SUPPORT
- else if(strncasecompare(cmd, "statvfs ", 8)) {
- state(data, SSH_SFTP_QUOTE_STATVFS);
+ else if(!strncmp(cmd, "statvfs ", 8)) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS);
return result;
}
-#endif
failf(data, "Unknown SFTP command");
Curl_safefree(sshc->quote_path1);
@@ -1161,6 +1094,7 @@ sftp_upload_init(struct Curl_easy *data,
LIBSSH2_SFTP_OPENFILE);
if(!sshc->sftp_handle) {
+ CURLcode result;
unsigned long sftperr;
int rc = libssh2_session_last_errno(sshc->ssh_session);
@@ -1177,12 +1111,10 @@ sftp_upload_init(struct Curl_easy *data,
sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */
if(sshc->secondCreateDirs) {
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = sftperr != LIBSSH2_FX_OK ?
- sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH;
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
failf(data, "Creating the dir/file failed: %s",
sftp_libssh2_strerror(sftperr));
- return CURLE_OK;
+ return sftp_libssh2_error_to_CURLE(sftperr);
}
if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) ||
(sftperr == LIBSSH2_FX_FAILURE) ||
@@ -1191,24 +1123,23 @@ sftp_upload_init(struct Curl_easy *data,
(strlen(sshp->path) > 1))) {
/* try to create the path remotely */
sshc->secondCreateDirs = 1;
- state(data, SSH_SFTP_CREATE_DIRS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_INIT);
return CURLE_OK;
}
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = sftperr != LIBSSH2_FX_OK ?
- sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH;
- if(!sshc->actualcode) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ result = sftp_libssh2_error_to_CURLE(sftperr);
+ if(!result) {
/* Sometimes, for some reason libssh2_sftp_last_error() returns zero
even though libssh2_sftp_open() failed previously! We need to
work around that! */
- sshc->actualcode = CURLE_SSH;
+ result = CURLE_SSH;
sftperr = LIBSSH2_FX_OK;
}
failf(data, "Upload failed: %s (%lu/%d)",
sftperr != LIBSSH2_FX_OK ?
sftp_libssh2_strerror(sftperr) : "ssh error",
sftperr, rc);
- return sshc->actualcode;
+ return result;
}
/* If we have a restart point then we need to seek to the correct
@@ -1262,7 +1193,8 @@ sftp_upload_init(struct Curl_easy *data,
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}
- SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
+ libssh2_sftp_seek64(sshc->sftp_handle,
+ (libssh2_uint64_t)data->state.resume_from);
}
if(data->state.infilesize > 0) {
data->req.size = data->state.infilesize;
@@ -1288,13 +1220,15 @@ sftp_upload_init(struct Curl_easy *data,
timeout here */
Curl_expire(data, 0, EXPIRE_RUN_NOW);
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
-static CURLcode
-sftp_pkey_init(struct Curl_easy *data,
- struct ssh_conn *sshc)
+/* make sure that this does not collide with an actual libssh2 error code */
+#define ERROR_LIBBSH2 1
+
+static CURLcode ssh_state_pkey_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
/*
* Check the supported auth types in the order I feel is most secure
@@ -1323,7 +1257,7 @@ sftp_pkey_init(struct Curl_easy *data,
if(!sshc->rsa)
out_of_memory = TRUE;
else if(stat(sshc->rsa, &sbuf)) {
- Curl_safefree(sshc->rsa);
+ free(sshc->rsa);
sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
if(!sshc->rsa)
out_of_memory = TRUE;
@@ -1337,10 +1271,10 @@ sftp_pkey_init(struct Curl_easy *data,
/* Nothing found; try the current dir. */
sshc->rsa = strdup("id_rsa");
if(sshc->rsa && stat(sshc->rsa, &sbuf)) {
- Curl_safefree(sshc->rsa);
+ free(sshc->rsa);
sshc->rsa = strdup("id_dsa");
if(sshc->rsa && stat(sshc->rsa, &sbuf)) {
- Curl_safefree(sshc->rsa);
+ free(sshc->rsa);
/* Out of guesses. Set to the empty string to avoid
* surprising info messages. */
sshc->rsa = strdup("");
@@ -1365,8 +1299,7 @@ sftp_pkey_init(struct Curl_easy *data,
if(out_of_memory || !sshc->rsa) {
Curl_safefree(sshc->rsa);
Curl_safefree(sshc->rsa_pub);
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
return CURLE_OUT_OF_MEMORY;
}
@@ -1378,12 +1311,12 @@ sftp_pkey_init(struct Curl_easy *data,
infof(data, "Using SSH public key file '%s'", sshc->rsa_pub);
infof(data, "Using SSH private key file '%s'", sshc->rsa);
- state(data, SSH_AUTH_PKEY);
+ myssh_state(data, sshc, SSH_AUTH_PKEY);
}
else {
- state(data, SSH_AUTH_PASS_INIT);
+ myssh_state(data, sshc, SSH_AUTH_PASS_INIT);
}
- return CURLE_OK;
+ return 0;
}
static CURLcode
@@ -1405,7 +1338,7 @@ sftp_quote_stat(struct Curl_easy *data,
sshc->acceptfail = TRUE;
}
- if(!strncasecompare(cmd, "chmod", 5)) {
+ if(!!strncmp(cmd, "chmod", 5)) {
/* Since chown and chgrp only set owner OR group but libssh2 wants to set
* them both at once, we need to obtain the current ownership first. This
* takes an extra protocol round trip.
@@ -1427,8 +1360,11 @@ sftp_quote_stat(struct Curl_easy *data,
}
/* Now set the new attributes... */
- if(strncasecompare(cmd, "chgrp", 5)) {
- sshp->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10);
+ if(!strncmp(cmd, "chgrp", 5)) {
+ const char *p = sshc->quote_path1;
+ curl_off_t gid;
+ (void)curlx_str_number(&p, &gid, ULONG_MAX);
+ sshp->quote_attrs.gid = (unsigned long)gid;
sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshp->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
!sshc->acceptfail) {
@@ -1436,18 +1372,23 @@ sftp_quote_stat(struct Curl_easy *data,
goto fail;
}
}
- else if(strncasecompare(cmd, "chmod", 5)) {
- sshp->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8);
- sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
+ else if(!strncmp(cmd, "chmod", 5)) {
+ curl_off_t perms;
+ const char *p = sshc->quote_path1;
/* permissions are octal */
- if(sshp->quote_attrs.permissions == 0 &&
- !ISDIGIT(sshc->quote_path1[0])) {
+ if(curlx_str_octal(&p, &perms, 07777)) {
failf(data, "Syntax error: chmod permissions not a number");
goto fail;
}
+
+ sshp->quote_attrs.permissions = (unsigned long)perms;
+ sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
}
- else if(strncasecompare(cmd, "chown", 5)) {
- sshp->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10);
+ else if(!strncmp(cmd, "chown", 5)) {
+ const char *p = sshc->quote_path1;
+ curl_off_t uid;
+ (void)curlx_str_number(&p, &uid, ULONG_MAX);
+ sshp->quote_attrs.uid = (unsigned long)uid;
sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshp->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
!sshc->acceptfail) {
@@ -1455,8 +1396,8 @@ sftp_quote_stat(struct Curl_easy *data,
goto fail;
}
}
- else if(strncasecompare(cmd, "atime", 5) ||
- strncasecompare(cmd, "mtime", 5)) {
+ else if(!strncmp(cmd, "atime", 5) ||
+ !strncmp(cmd, "mtime", 5)) {
time_t date = Curl_getdate_capped(sshc->quote_path1);
bool fail = FALSE;
@@ -1473,7 +1414,7 @@ sftp_quote_stat(struct Curl_easy *data,
#endif
if(fail)
goto fail;
- if(strncasecompare(cmd, "atime", 5))
+ if(!strncmp(cmd, "atime", 5))
sshp->quote_attrs.atime = (unsigned long)date;
else /* mtime */
sshp->quote_attrs.mtime = (unsigned long)date;
@@ -1482,7 +1423,7 @@ sftp_quote_stat(struct Curl_easy *data,
}
/* Now send the completed structure... */
- state(data, SSH_SFTP_QUOTE_SETSTAT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_SETSTAT);
return CURLE_OK;
fail:
Curl_safefree(sshc->quote_path1);
@@ -1526,20 +1467,19 @@ sftp_download_stat(struct Curl_easy *data,
}
if(data->state.use_range) {
curl_off_t from, to;
- char *ptr;
- char *ptr2;
- CURLofft to_t;
- CURLofft from_t;
+ const char *p = data->state.range;
+ int to_t, from_t;
- from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
- if(from_t == CURL_OFFT_FLOW)
+ from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX);
+ if(from_t == STRE_OVERFLOW)
return CURLE_RANGE_ERROR;
- while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
- ptr++;
- to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
- if(to_t == CURL_OFFT_FLOW)
+ curlx_str_passblanks(&p);
+ (void)curlx_str_single(&p, '-');
+
+ to_t = curlx_str_numblanks(&p, &to);
+ if(to_t == STRE_OVERFLOW)
return CURLE_RANGE_ERROR;
- if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
+ if((to_t == STRE_NO_NUM) /* no "to" value given */
|| (to >= size)) {
to = size - 1;
}
@@ -1563,7 +1503,7 @@ sftp_download_stat(struct Curl_easy *data,
size = to - from + 1;
}
- SFTP_SEEK(sshc->sftp_handle, from);
+ libssh2_sftp_seek64(sshc->sftp_handle, (libssh2_uint64_t)from);
}
data->req.size = size;
data->req.maxdownload = size;
@@ -1596,7 +1536,8 @@ sftp_download_stat(struct Curl_easy *data,
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);
+ libssh2_sftp_seek64(sshc->sftp_handle,
+ (libssh2_uint64_t)data->state.resume_from);
}
/* Setup the actual download */
@@ -1604,7 +1545,7 @@ sftp_download_stat(struct Curl_easy *data,
/* no data to transfer */
Curl_xfer_setup_nop(data);
infof(data, "File already completely downloaded");
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
@@ -1616,7 +1557,7 @@ sftp_download_stat(struct Curl_easy *data,
out writableable as the underlying libssh2 recv function will deal
with both accordingly */
data->state.select_bits = CURL_CSELECT_IN;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
@@ -1644,44 +1585,1107 @@ static CURLcode sftp_readdir(struct Curl_easy *data,
sshp->readdir_filename,
readdir_len);
if(!result)
- result = Curl_client_write(data, CLIENTWRITE_BODY,
- (char *)"\n", 1);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
if(result)
return result;
}
else {
- result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry);
+ result = curlx_dyn_add(&sshp->readdir, sshp->readdir_longentry);
if(!result) {
if((sshp->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFLNK)) {
- Curl_dyn_init(&sshp->readdir_link, CURL_PATH_MAX);
- result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path,
- sshp->readdir_filename);
- state(data, SSH_SFTP_READDIR_LINK);
+ result = curlx_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path,
+ sshp->readdir_filename);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_LINK);
}
else {
- state(data, SSH_SFTP_READDIR_BOTTOM);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
}
}
return result;
}
}
- else if(rc == 0) {
- state(data, SSH_SFTP_READDIR_DONE);
+ else if(!rc) {
+ myssh_state(data, sshc, SSH_SFTP_READDIR_DONE);
}
- else if(rc < 0) {
+ else {
unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- result = sftp_libssh2_error_to_CURLE(sftperr);
- sshc->actualcode = result ? result : CURLE_SSH;
+ result = sftperr ? sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH;
failf(data, "Could not open remote file for reading: %s :: %d",
sftp_libssh2_strerror(sftperr),
libssh2_session_last_errno(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
}
return result;
}
+
+static CURLcode ssh_state_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ CURLcode result;
+ sshc->secondCreateDirs = 0;
+ sshc->nextstate = SSH_NO_STATE;
+
+ /* Set libssh2 to non-blocking, since everything internally is
+ non-blocking */
+ libssh2_session_set_blocking(sshc->ssh_session, 0);
+
+ result = ssh_force_knownhost_key_type(data, sshc);
+ if(result)
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ else
+ myssh_state(data, sshc, SSH_S_STARTUP);
+ return result;
+}
+
+static CURLcode ssh_state_startup(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ struct connectdata *conn = data->conn;
+ int rc = libssh2_session_handshake(sshc->ssh_session,
+ conn->sock[FIRSTSOCKET]);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
+ failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg);
+
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_FAILED_INIT;
+ }
+
+ myssh_state(data, sshc, SSH_HOSTKEY);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_hostkey(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /*
+ * Before we authenticate we should check the hostkey's fingerprint
+ * against our known hosts. How that is handled (reading from file,
+ * whatever) is up to us.
+ */
+ CURLcode result = ssh_check_fingerprint(data, sshc);
+ if(!result)
+ myssh_state(data, sshc, SSH_AUTHLIST);
+ return result;
+}
+
+static CURLcode ssh_state_authlist(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /*
+ * Figure out authentication methods
+ * NB: As soon as we have provided a username to an openssh server we
+ * must never change it later. Thus, always specify the correct username
+ * here, even though the libssh2 docs kind of indicate that it should be
+ * possible to get a 'generic' list (not user-specific) of authentication
+ * methods, presumably with a blank username. That will not work in my
+ * experience.
+ * So always specify it here.
+ */
+ struct connectdata *conn = data->conn;
+ sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
+ conn->user,
+ curlx_uztoui(strlen(conn->user)));
+
+ if(!sshc->authlist) {
+ int rc;
+ if(libssh2_userauth_authenticated(sshc->ssh_session)) {
+ sshc->authed = TRUE;
+ infof(data, "SSH user accepted with no authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ return CURLE_OK;
+ }
+ rc = libssh2_session_last_errno(sshc->ssh_session);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return libssh2_session_error_to_CURLE(rc);
+ }
+ infof(data, "SSH authentication methods available: %s",
+ sshc->authlist);
+
+ myssh_state(data, sshc, SSH_AUTH_PKEY_INIT);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_pkey(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* The function below checks if the files exists, no need to stat() here.
+ */
+ struct connectdata *conn = data->conn;
+ int rc =
+ libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
+ conn->user,
+ curlx_uztoui(
+ strlen(conn->user)),
+ sshc->rsa_pub,
+ sshc->rsa, sshc->passphrase);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ Curl_safefree(sshc->rsa_pub);
+ Curl_safefree(sshc->rsa);
+
+ if(rc == 0) {
+ sshc->authed = TRUE;
+ infof(data, "Initialized SSH public key authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ }
+ else {
+ char *err_msg = NULL;
+ char unknown[] = "Reason unknown (-1)";
+ if(rc == -1) {
+ /* No error message has been set and the last set error message, if
+ any, is from a previous error so ignore it. #11837 */
+ err_msg = unknown;
+ }
+ else {
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ }
+ infof(data, "SSH public key authentication failed: %s", err_msg);
+ myssh_state(data, sshc, SSH_AUTH_PASS_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_pass_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
+ (strstr(sshc->authlist, "password") != NULL)) {
+ myssh_state(data, sshc, SSH_AUTH_PASS);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_HOST_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_pass(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ struct connectdata *conn = data->conn;
+ int rc =
+ libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
+ curlx_uztoui(strlen(conn->user)),
+ conn->passwd,
+ curlx_uztoui(strlen(conn->passwd)),
+ NULL);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return CURLE_AGAIN;
+ }
+ if(rc == 0) {
+ sshc->authed = TRUE;
+ infof(data, "Initialized password authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_HOST_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_host_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
+ (strstr(sshc->authlist, "hostbased") != NULL)) {
+ myssh_state(data, sshc, SSH_AUTH_HOST);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_AGENT_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = 0;
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
+ && (strstr(sshc->authlist, "publickey") != NULL)) {
+
+ /* Connect to the ssh-agent */
+ /* The agent could be shared by a curl thread i believe
+ but nothing obvious as keys can be added/removed at any time */
+ if(!sshc->ssh_agent) {
+ sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
+ if(!sshc->ssh_agent) {
+ infof(data, "Could not create agent object");
+
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ return CURLE_OK;
+ }
+ }
+
+ rc = libssh2_agent_connect(sshc->ssh_agent);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+ if(rc < 0) {
+ infof(data, "Failure connecting to agent");
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_AGENT_LIST);
+ }
+ }
+ else
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_agent_list(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = libssh2_agent_list_identities(sshc->ssh_agent);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+ if(rc < 0) {
+ infof(data, "Failure requesting identities to agent");
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_AGENT);
+ sshc->sshagent_prev_identity = NULL;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_agent(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* as prev_identity evolves only after an identity user auth finished we
+ can safely request it again as long as EAGAIN is returned here or by
+ libssh2_agent_userauth */
+ int rc = libssh2_agent_get_identity(sshc->ssh_agent,
+ &sshc->sshagent_identity,
+ sshc->sshagent_prev_identity);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc == 0) {
+ struct connectdata *conn = data->conn;
+ rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
+ sshc->sshagent_identity);
+
+ if(rc < 0) {
+ if(rc != LIBSSH2_ERROR_EAGAIN) {
+ /* tried and failed? go to next identity */
+ sshc->sshagent_prev_identity = sshc->sshagent_identity;
+ }
+ return CURLE_OK;
+ }
+ }
+
+ if(rc < 0)
+ infof(data, "Failure requesting identities to agent");
+ else if(rc == 1)
+ infof(data, "No identity would match");
+
+ if(rc == LIBSSH2_ERROR_NONE) {
+ sshc->authed = TRUE;
+ infof(data, "Agent based authentication successful");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_key_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
+ && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
+ myssh_state(data, sshc, SSH_AUTH_KEY);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_key(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* Authentication failed. Continue with keyboard-interactive now. */
+ struct connectdata *conn = data->conn;
+ int rc =
+ libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
+ conn->user,
+ curlx_uztoui(
+ strlen(conn->user)),
+ &kbd_callback);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc == 0) {
+ sshc->authed = TRUE;
+ infof(data, "Initialized keyboard interactive authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ return CURLE_OK;
+ }
+ return CURLE_LOGIN_DENIED;
+}
+
+static CURLcode ssh_state_auth_done(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ struct connectdata *conn = data->conn;
+ if(!sshc->authed) {
+ failf(data, "Authentication failure");
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_LOGIN_DENIED;
+ }
+
+ /*
+ * At this point we have an authenticated ssh session.
+ */
+ infof(data, "Authentication complete");
+
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
+
+ conn->sockfd = conn->sock[FIRSTSOCKET];
+ conn->writesockfd = CURL_SOCKET_BAD;
+
+ if(conn->handler->protocol == CURLPROTO_SFTP) {
+ myssh_state(data, sshc, SSH_SFTP_INIT);
+ return CURLE_OK;
+ }
+ infof(data, "SSH CONNECT phase done");
+ myssh_state(data, sshc, SSH_STOP);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /*
+ * Start the libssh2 sftp session
+ */
+ sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
+ if(!sshc->sftp_session) {
+ char *err_msg = NULL;
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ failf(data, "Failure initializing sftp session: %s", err_msg);
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_FAILED_INIT;
+ }
+ myssh_state(data, sshc, SSH_SFTP_REALPATH);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_realpath(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /*
+ * Get the "home" directory
+ */
+ int rc = libssh2_sftp_symlink_ex(sshc->sftp_session,
+ ".", curlx_uztoui(strlen(".")),
+ sshp->readdir_filename, CURL_PATH_MAX,
+ LIBSSH2_SFTP_REALPATH);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc > 0) {
+ /* It seems that this string is not always null-terminated */
+ sshp->readdir_filename[rc] = '\0';
+ free(sshc->homedir);
+ sshc->homedir = strdup(sshp->readdir_filename);
+ if(!sshc->homedir) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ free(data->state.most_recent_ftp_entrypath);
+ data->state.most_recent_ftp_entrypath = strdup(sshc->homedir);
+ if(!data->state.most_recent_ftp_entrypath)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ /* Return the error type */
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ CURLcode result;
+ if(sftperr)
+ result = sftp_libssh2_error_to_CURLE(sftperr);
+ else
+ /* in this case, the error was not in the SFTP level but for example
+ a time-out or similar */
+ result = CURLE_SSH;
+ DEBUGF(infof(data, "error = %lu makes libcurl = %d",
+ sftperr, (int)result));
+ myssh_state(data, sshc, SSH_STOP);
+ return result;
+ }
+
+ /* This is the last step in the SFTP connect phase. Do note that while
+ we get the homedir here, we get the "workingpath" in the DO action
+ since the homedir will remain the same between request but the
+ working path will not. */
+ DEBUGF(infof(data, "SSH CONNECT phase done"));
+ myssh_state(data, sshc, SSH_STOP);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
+ if(result) {
+ myssh_state(data, sshc, SSH_STOP);
+ return result;
+ }
+
+ if(data->set.quote) {
+ infof(data, "Sending quote commands");
+ sshc->quote_item = data->set.quote;
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
+ }
+ else {
+ myssh_state(data, sshc, SSH_SFTP_GETINFO);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_postquote_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ if(data->set.postquote) {
+ infof(data, "Sending quote commands");
+ sshc->quote_item = data->set.postquote;
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
+ }
+ else {
+ myssh_state(data, sshc, SSH_STOP);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /* Send quote commands */
+ CURLcode result = sftp_quote(data, sshc, sshp);
+ if(result) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ }
+ return result;
+}
+
+static CURLcode ssh_state_sftp_next_quote(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+
+ sshc->quote_item = sshc->quote_item->next;
+
+ if(sshc->quote_item) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
+ }
+ else {
+ if(sshc->nextstate != SSH_NO_STATE) {
+ myssh_state(data, sshc, sshc->nextstate);
+ sshc->nextstate = SSH_NO_STATE;
+ }
+ else {
+ myssh_state(data, sshc, SSH_SFTP_GETINFO);
+ }
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_stat(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
+ bool *blockp)
+{
+ CURLcode result = sftp_quote_stat(data, sshc, sshp, blockp);
+ if(result) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ }
+ return result;
+}
+
+static CURLcode ssh_state_sftp_quote_setstat(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ int rc =
+ libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_SETSTAT,
+ &sshp->quote_attrs);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Attempt to set SFTP stats failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_symlink(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc =
+ libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_SYMLINK);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "symlink command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_mkdir(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ (long)data->set.new_directory_perms);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "mkdir command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_rename(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc =
+ libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_RENAME_OVERWRITE |
+ LIBSSH2_SFTP_RENAME_ATOMIC |
+ LIBSSH2_SFTP_RENAME_NATIVE);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "rename command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_rmdir(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)));
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "rmdir command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_unlink(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)));
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_statvfs(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ LIBSSH2_SFTP_STATVFS statvfs;
+ int rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ &statvfs);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "statvfs command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ else if(rc == 0) {
+#ifdef _MSC_VER
+#define CURL_LIBSSH2_VFS_SIZE_MASK "I64u"
+#else
+#define CURL_LIBSSH2_VFS_SIZE_MASK "llu"
+#endif
+ CURLcode result;
+ char *tmp = aprintf("statvfs:\n"
+ "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n",
+ statvfs.f_bsize, statvfs.f_frsize,
+ statvfs.f_blocks, statvfs.f_bfree,
+ statvfs.f_bavail, statvfs.f_files,
+ statvfs.f_ffree, statvfs.f_favail,
+ statvfs.f_fsid, statvfs.f_flag,
+ statvfs.f_namemax);
+ if(!tmp) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
+ free(tmp);
+ if(result) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return result;
+ }
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_create_dirs_mkdir(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /* 'mode' - parameter is preliminary - default to 0644 */
+ int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ (long)data->set.new_directory_perms);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ *sshc->slash_pos = '/';
+ ++sshc->slash_pos;
+ if(rc < 0) {
+ /*
+ * Abort if failure was not that the dir already exists or the
+ * permission was denied (creation might succeed further down the
+ * path) - retry on unspecific FAILURE also
+ */
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
+ (sftperr != LIBSSH2_FX_FAILURE) &&
+ (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ return sftp_libssh2_error_to_CURLE(sftperr);
+ }
+ }
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_readdir_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ Curl_pgrsSetDownloadSize(data, -1);
+ if(data->req.no_body) {
+ myssh_state(data, sshc, SSH_STOP);
+ return CURLE_OK;
+ }
+
+ /*
+ * This is a directory that we are trying to get, so produce a directory
+ * listing
+ */
+ sshc->sftp_handle =
+ libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ 0, 0, LIBSSH2_SFTP_OPENDIR);
+ if(!sshc->sftp_handle) {
+ unsigned long sftperr;
+ if(libssh2_session_last_errno(sshc->ssh_session) == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ failf(data, "Could not open directory for reading: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ return sftp_libssh2_error_to_CURLE(sftperr);
+ }
+ myssh_state(data, sshc, SSH_SFTP_READDIR);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_readdir_link(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ CURLcode result;
+ int rc =
+ libssh2_sftp_symlink_ex(sshc->sftp_session,
+ curlx_dyn_ptr(&sshp->readdir_link),
+ (unsigned int)
+ curlx_dyn_len(&sshp->readdir_link),
+ sshp->readdir_filename,
+ CURL_PATH_MAX, LIBSSH2_SFTP_READLINK);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ curlx_dyn_free(&sshp->readdir_link);
+
+ /* append filename and extra output */
+ result = curlx_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename);
+ if(result)
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ else
+ myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
+ return result;
+}
+
+static CURLcode ssh_state_scp_download_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ curl_off_t bytecount;
+
+ /*
+ * We must check the remote file; if it is a directory no values will
+ * be set in sb
+ */
+
+ /*
+ * If support for >2GB files exists, use it.
+ */
+
+ /* get a fresh new channel from the ssh layer */
+#if LIBSSH2_VERSION_NUM < 0x010700
+ struct stat sb;
+ memset(&sb, 0, sizeof(struct stat));
+ sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
+ sshp->path, &sb);
+#else
+ libssh2_struct_stat sb;
+ memset(&sb, 0, sizeof(libssh2_struct_stat));
+ sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session,
+ sshp->path, &sb);
+#endif
+
+ if(!sshc->ssh_channel) {
+ int ssh_err;
+ char *err_msg = NULL;
+
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0));
+ failf(data, "%s", err_msg);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
+ return libssh2_session_error_to_CURLE(ssh_err);
+ }
+
+ /* download data */
+ bytecount = (curl_off_t)sb.st_size;
+ data->req.maxdownload = (curl_off_t)sb.st_size;
+ Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE);
+
+ /* not set by Curl_xfer_setup to preserve keepon bits */
+ data->conn->writesockfd = data->conn->sockfd;
+
+ /* 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 */
+ data->state.select_bits = CURL_CSELECT_IN;
+
+ myssh_state(data, sshc, SSH_STOP);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_close(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ int rc = 0;
+ if(sshc->sftp_handle) {
+ rc = libssh2_sftp_close(sshc->sftp_handle);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+ }
+ sshc->sftp_handle = NULL;
+ }
+
+ Curl_safefree(sshp->path);
+
+ DEBUGF(infof(data, "SFTP DONE done"));
+
+ /* 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 &&
+ sshc->nextstate != SSH_SFTP_CLOSE) {
+ myssh_state(data, sshc, sshc->nextstate);
+ sshc->nextstate = SSH_SFTP_CLOSE;
+ }
+ else
+ myssh_state(data, sshc, SSH_STOP);
+
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_shutdown(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* 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 */
+ int rc = 0;
+ if(sshc->sftp_handle) {
+ rc = libssh2_sftp_close(sshc->sftp_handle);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
+ NULL, 0);
+ infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+ }
+ sshc->sftp_handle = NULL;
+ }
+ if(sshc->sftp_session) {
+ rc = libssh2_sftp_shutdown(sshc->sftp_session);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc < 0) {
+ infof(data, "Failed to stop libssh2 sftp subsystem");
+ }
+ sshc->sftp_session = NULL;
+ }
+
+ Curl_safefree(sshc->homedir);
+
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_download_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /*
+ * Work on getting the specified file
+ */
+ sshc->sftp_handle =
+ libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ LIBSSH2_FXF_READ, (long)data->set.new_file_perms,
+ LIBSSH2_SFTP_OPENFILE);
+ if(!sshc->sftp_handle) {
+ unsigned long sftperr;
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN) {
+ return CURLE_AGAIN;
+ }
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ failf(data, "Could not open remote file for reading: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ return sftp_libssh2_error_to_CURLE(sftperr);
+ }
+ myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /*
+ * 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 =
+ libssh2_scp_send64(sshc->ssh_session, sshp->path,
+ (int)data->set.new_file_perms,
+ (libssh2_int64_t)data->state.infilesize, 0, 0);
+ if(!sshc->ssh_channel) {
+ int ssh_err;
+ char *err_msg = NULL;
+ CURLcode result;
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0));
+ failf(data, "%s", err_msg);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
+ result = libssh2_session_error_to_CURLE(ssh_err);
+
+ /* Map generic errors to upload failed */
+ if(result == CURLE_SSH ||
+ result == CURLE_REMOTE_FILE_NOT_FOUND)
+ result = CURLE_UPLOAD_FAILED;
+ return result;
+ }
+
+ /* upload data */
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
+
+ /* not set by Curl_xfer_setup to preserve keepon bits */
+ data->conn->sockfd = data->conn->writesockfd;
+
+ /* store this original bitmask setup to use later on if we cannot
+ figure out a "real" bitmask */
+ sshc->orig_waitfor = data->req.keepon;
+
+ /* 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 */
+ data->state.select_bits = CURL_CSELECT_OUT;
+
+ myssh_state(data, sshc, SSH_STOP);
+
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_session_disconnect(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* during weird times when we have been prematurely aborted, the channel
+ is still alive when we reach this state and we MUST kill the channel
+ properly first */
+ int rc = 0;
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_free(sshc->ssh_channel);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_OK;
+
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to free libssh2 scp subsystem: %d %s",
+ rc, err_msg);
+ }
+ sshc->ssh_channel = NULL;
+ }
+
+ if(sshc->ssh_session) {
+ rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to disconnect libssh2 session: %d %s",
+ rc, err_msg);
+ }
+ }
+
+ Curl_safefree(sshc->homedir);
+
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_OK;
+}
/*
* ssh_statemachine() runs the SSH state machine as far as it can without
* blocking and without reaching the end. The data the pointer 'block' points
@@ -1689,723 +2693,195 @@ static CURLcode sftp_readdir(struct Curl_easy *data,
* meaning it wants to be called again when the socket is ready
*/
-static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
+static CURLcode ssh_statemachine(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
+ bool *block)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct SSHPROTO *sshp = data->req.p.ssh;
- struct ssh_conn *sshc = &conn->proto.sshc;
-
- int rc = LIBSSH2_ERROR_NONE;
*block = 0; /* we are not blocking by default */
do {
switch(sshc->state) {
case SSH_INIT:
- sshc->secondCreateDirs = 0;
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_OK;
-
- /* Set libssh2 to non-blocking, since everything internally is
- non-blocking */
- libssh2_session_set_blocking(sshc->ssh_session, 0);
-
- result = ssh_force_knownhost_key_type(data);
- if(result) {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = result;
+ result = ssh_state_init(data, sshc);
+ if(result)
break;
- }
-
- state(data, SSH_S_STARTUP);
FALLTHROUGH();
case SSH_S_STARTUP:
- rc = session_startup(sshc->ssh_session, conn->sock[FIRSTSOCKET]);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
- failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg);
-
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_FAILED_INIT;
+ result = ssh_state_startup(data, sshc);
+ if(result)
break;
- }
-
- state(data, SSH_HOSTKEY);
-
FALLTHROUGH();
+
case SSH_HOSTKEY:
- /*
- * Before we authenticate we should check the hostkey's fingerprint
- * against our known hosts. How that is handled (reading from file,
- * whatever) is up to us.
- */
- result = ssh_check_fingerprint(data);
- if(!result)
- state(data, SSH_AUTHLIST);
- /* ssh_check_fingerprint sets state appropriately on error */
+ result = ssh_state_hostkey(data, sshc);
break;
case SSH_AUTHLIST:
- /*
- * Figure out authentication methods
- * NB: As soon as we have provided a username to an openssh server we
- * must never change it later. Thus, always specify the correct username
- * here, even though the libssh2 docs kind of indicate that it should be
- * possible to get a 'generic' list (not user-specific) of authentication
- * methods, presumably with a blank username. That will not work in my
- * experience.
- * So always specify it here.
- */
- sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
- conn->user,
- curlx_uztoui(strlen(conn->user)));
-
- if(!sshc->authlist) {
- if(libssh2_userauth_authenticated(sshc->ssh_session)) {
- sshc->authed = TRUE;
- infof(data, "SSH user accepted with no authentication");
- state(data, SSH_AUTH_DONE);
- break;
- }
- rc = libssh2_session_last_errno(sshc->ssh_session);
- if(rc == LIBSSH2_ERROR_EAGAIN)
- rc = LIBSSH2_ERROR_EAGAIN;
- else {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = libssh2_session_error_to_CURLE(rc);
- }
- break;
- }
- infof(data, "SSH authentication methods available: %s",
- sshc->authlist);
-
- state(data, SSH_AUTH_PKEY_INIT);
+ result = ssh_state_authlist(data, sshc);
break;
case SSH_AUTH_PKEY_INIT:
- result = sftp_pkey_init(data, sshc);
+ result = ssh_state_pkey_init(data, sshc);
break;
case SSH_AUTH_PKEY:
- /* The function below checks if the files exists, no need to stat() here.
- */
- rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
- conn->user,
- curlx_uztoui(
- strlen(conn->user)),
- sshc->rsa_pub,
- sshc->rsa, sshc->passphrase);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
-
- Curl_safefree(sshc->rsa_pub);
- Curl_safefree(sshc->rsa);
-
- if(rc == 0) {
- sshc->authed = TRUE;
- infof(data, "Initialized SSH public key authentication");
- state(data, SSH_AUTH_DONE);
- }
- else {
- char *err_msg = NULL;
- char unknown[] = "Reason unknown (-1)";
- if(rc == -1) {
- /* No error message has been set and the last set error message, if
- any, is from a previous error so ignore it. #11837 */
- err_msg = unknown;
- }
- else {
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- }
- infof(data, "SSH public key authentication failed: %s", err_msg);
- state(data, SSH_AUTH_PASS_INIT);
- rc = 0; /* clear rc and continue */
- }
+ result = ssh_state_auth_pkey(data, sshc);
break;
case SSH_AUTH_PASS_INIT:
- if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
- (strstr(sshc->authlist, "password") != NULL)) {
- state(data, SSH_AUTH_PASS);
- }
- else {
- state(data, SSH_AUTH_HOST_INIT);
- rc = 0; /* clear rc and continue */
- }
+ result = ssh_state_auth_pass_init(data, sshc);
break;
case SSH_AUTH_PASS:
- rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
- curlx_uztoui(strlen(conn->user)),
- conn->passwd,
- curlx_uztoui(strlen(conn->passwd)),
- NULL);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc == 0) {
- sshc->authed = TRUE;
- infof(data, "Initialized password authentication");
- state(data, SSH_AUTH_DONE);
- }
- else {
- state(data, SSH_AUTH_HOST_INIT);
- rc = 0; /* clear rc and continue */
- }
+ result = ssh_state_auth_pass(data, sshc);
break;
case SSH_AUTH_HOST_INIT:
- if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
- (strstr(sshc->authlist, "hostbased") != NULL)) {
- state(data, SSH_AUTH_HOST);
- }
- else {
- state(data, SSH_AUTH_AGENT_INIT);
- }
+ result = ssh_state_auth_host_init(data, sshc);
break;
case SSH_AUTH_HOST:
- state(data, SSH_AUTH_AGENT_INIT);
+ myssh_state(data, sshc, SSH_AUTH_AGENT_INIT);
break;
case SSH_AUTH_AGENT_INIT:
-#ifdef HAVE_LIBSSH2_AGENT_API
- if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
- && (strstr(sshc->authlist, "publickey") != NULL)) {
-
- /* Connect to the ssh-agent */
- /* The agent could be shared by a curl thread i believe
- but nothing obvious as keys can be added/removed at any time */
- if(!sshc->ssh_agent) {
- sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
- if(!sshc->ssh_agent) {
- infof(data, "Could not create agent object");
-
- state(data, SSH_AUTH_KEY_INIT);
- break;
- }
- }
-
- rc = libssh2_agent_connect(sshc->ssh_agent);
- if(rc == LIBSSH2_ERROR_EAGAIN)
- break;
- if(rc < 0) {
- infof(data, "Failure connecting to agent");
- state(data, SSH_AUTH_KEY_INIT);
- rc = 0; /* clear rc and continue */
- }
- else {
- state(data, SSH_AUTH_AGENT_LIST);
- }
- }
- else
-#endif /* HAVE_LIBSSH2_AGENT_API */
- state(data, SSH_AUTH_KEY_INIT);
+ result = ssh_state_auth_agent_init(data, sshc);
break;
case SSH_AUTH_AGENT_LIST:
-#ifdef HAVE_LIBSSH2_AGENT_API
- rc = libssh2_agent_list_identities(sshc->ssh_agent);
-
- if(rc == LIBSSH2_ERROR_EAGAIN)
- break;
- if(rc < 0) {
- infof(data, "Failure requesting identities to agent");
- state(data, SSH_AUTH_KEY_INIT);
- rc = 0; /* clear rc and continue */
- }
- else {
- state(data, SSH_AUTH_AGENT);
- sshc->sshagent_prev_identity = NULL;
- }
-#endif
+ result = ssh_state_auth_agent_list(data, sshc);
break;
case SSH_AUTH_AGENT:
-#ifdef HAVE_LIBSSH2_AGENT_API
- /* as prev_identity evolves only after an identity user auth finished we
- can safely request it again as long as EAGAIN is returned here or by
- libssh2_agent_userauth */
- rc = libssh2_agent_get_identity(sshc->ssh_agent,
- &sshc->sshagent_identity,
- sshc->sshagent_prev_identity);
- if(rc == LIBSSH2_ERROR_EAGAIN)
- break;
-
- if(rc == 0) {
- rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
- sshc->sshagent_identity);
-
- if(rc < 0) {
- if(rc != LIBSSH2_ERROR_EAGAIN) {
- /* tried and failed? go to next identity */
- sshc->sshagent_prev_identity = sshc->sshagent_identity;
- }
- break;
- }
- }
-
- if(rc < 0)
- infof(data, "Failure requesting identities to agent");
- else if(rc == 1)
- infof(data, "No identity would match");
-
- if(rc == LIBSSH2_ERROR_NONE) {
- sshc->authed = TRUE;
- infof(data, "Agent based authentication successful");
- state(data, SSH_AUTH_DONE);
- }
- else {
- state(data, SSH_AUTH_KEY_INIT);
- rc = 0; /* clear rc and continue */
- }
-#endif
+ result = ssh_state_auth_agent(data, sshc);
break;
case SSH_AUTH_KEY_INIT:
- if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
- && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
- state(data, SSH_AUTH_KEY);
- }
- else {
- state(data, SSH_AUTH_DONE);
- }
+ result = ssh_state_auth_key_init(data, sshc);
break;
case SSH_AUTH_KEY:
- /* Authentication failed. Continue with keyboard-interactive now. */
- rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
- conn->user,
- curlx_uztoui(
- strlen(conn->user)),
- &kbd_callback);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc == 0) {
- sshc->authed = TRUE;
- infof(data, "Initialized keyboard interactive authentication");
- }
- state(data, SSH_AUTH_DONE);
+ result = ssh_state_auth_key(data, sshc);
break;
case SSH_AUTH_DONE:
- if(!sshc->authed) {
- failf(data, "Authentication failure");
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_LOGIN_DENIED;
- break;
- }
-
- /*
- * At this point we have an authenticated ssh session.
- */
- infof(data, "Authentication complete");
-
- Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
-
- conn->sockfd = conn->sock[FIRSTSOCKET];
- conn->writesockfd = CURL_SOCKET_BAD;
-
- if(conn->handler->protocol == CURLPROTO_SFTP) {
- state(data, SSH_SFTP_INIT);
- break;
- }
- infof(data, "SSH CONNECT phase done");
- state(data, SSH_STOP);
+ result = ssh_state_auth_done(data, sshc);
break;
case SSH_SFTP_INIT:
- /*
- * Start the libssh2 sftp session
- */
- sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
- if(!sshc->sftp_session) {
- char *err_msg = NULL;
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
-
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- failf(data, "Failure initializing sftp session: %s", err_msg);
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_FAILED_INIT;
- break;
- }
- state(data, SSH_SFTP_REALPATH);
+ result = ssh_state_sftp_init(data, sshc);
break;
case SSH_SFTP_REALPATH:
- /*
- * Get the "home" directory
- */
- rc = sftp_libssh2_realpath(sshc->sftp_session, ".",
- sshp->readdir_filename, CURL_PATH_MAX);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc > 0) {
- /* It seems that this string is not always NULL terminated */
- sshp->readdir_filename[rc] = '\0';
- sshc->homedir = strdup(sshp->readdir_filename);
- if(!sshc->homedir) {
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = CURLE_OUT_OF_MEMORY;
- break;
- }
- data->state.most_recent_ftp_entrypath = sshc->homedir;
- }
- else {
- /* Return the error type */
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- if(sftperr)
- result = sftp_libssh2_error_to_CURLE(sftperr);
- else
- /* in this case, the error was not in the SFTP level but for example
- a time-out or similar */
- result = CURLE_SSH;
- sshc->actualcode = result;
- DEBUGF(infof(data, "error = %lu makes libcurl = %d",
- sftperr, (int)result));
- state(data, SSH_STOP);
- break;
- }
-
- /* This is the last step in the SFTP connect phase. Do note that while
- we get the homedir here, we get the "workingpath" in the DO action
- since the homedir will remain the same between request but the
- working path will not. */
- DEBUGF(infof(data, "SSH CONNECT phase done"));
- state(data, SSH_STOP);
- break;
+ result = ssh_state_sftp_realpath(data, sshc, sshp);
+ break;
case SSH_SFTP_QUOTE_INIT:
-
- result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
- if(result) {
- sshc->actualcode = result;
- state(data, SSH_STOP);
- break;
- }
-
- if(data->set.quote) {
- infof(data, "Sending quote commands");
- sshc->quote_item = data->set.quote;
- state(data, SSH_SFTP_QUOTE);
- }
- else {
- state(data, SSH_SFTP_GETINFO);
- }
+ result = ssh_state_sftp_quote_init(data, sshc, sshp);
break;
case SSH_SFTP_POSTQUOTE_INIT:
- if(data->set.postquote) {
- infof(data, "Sending quote commands");
- sshc->quote_item = data->set.postquote;
- state(data, SSH_SFTP_QUOTE);
- }
- else {
- state(data, SSH_STOP);
- }
+ result = ssh_state_sftp_postquote_init(data, sshc);
break;
case SSH_SFTP_QUOTE:
- /* Send quote commands */
- result = sftp_quote(data, sshc, sshp);
- if(result) {
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
- }
+ result = ssh_state_sftp_quote(data, sshc, sshp);
break;
case SSH_SFTP_NEXT_QUOTE:
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
-
- sshc->quote_item = sshc->quote_item->next;
-
- if(sshc->quote_item) {
- state(data, SSH_SFTP_QUOTE);
- }
- else {
- if(sshc->nextstate != SSH_NO_STATE) {
- state(data, sshc->nextstate);
- sshc->nextstate = SSH_NO_STATE;
- }
- else {
- state(data, SSH_SFTP_GETINFO);
- }
- }
+ result = ssh_state_sftp_next_quote(data, sshc);
break;
case SSH_SFTP_QUOTE_STAT:
- result = sftp_quote_stat(data, sshc, sshp, block);
- if(result) {
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
- }
+ result = ssh_state_sftp_quote_stat(data, sshc, sshp, block);
break;
case SSH_SFTP_QUOTE_SETSTAT:
- rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
- curlx_uztoui(strlen(sshc->quote_path2)),
- LIBSSH2_SFTP_SETSTAT,
- &sshp->quote_attrs);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
- failf(data, "Attempt to set SFTP stats failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_setstat(data, sshc, sshp);
break;
case SSH_SFTP_QUOTE_SYMLINK:
- rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)),
- sshc->quote_path2,
- curlx_uztoui(strlen(sshc->quote_path2)),
- LIBSSH2_SFTP_SYMLINK);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
- failf(data, "symlink command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_symlink(data, sshc);
break;
case SSH_SFTP_QUOTE_MKDIR:
- rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)),
- (long)data->set.new_directory_perms);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- failf(data, "mkdir command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_mkdir(data, sshc);
break;
case SSH_SFTP_QUOTE_RENAME:
- rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)),
- sshc->quote_path2,
- curlx_uztoui(strlen(sshc->quote_path2)),
- LIBSSH2_SFTP_RENAME_OVERWRITE |
- LIBSSH2_SFTP_RENAME_ATOMIC |
- LIBSSH2_SFTP_RENAME_NATIVE);
-
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
- failf(data, "rename command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_rename(data, sshc);
break;
case SSH_SFTP_QUOTE_RMDIR:
- rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)));
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- failf(data, "rmdir command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_rmdir(data, sshc);
break;
case SSH_SFTP_QUOTE_UNLINK:
- rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)));
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_unlink(data, sshc);
break;
-#ifdef HAS_STATVFS_SUPPORT
case SSH_SFTP_QUOTE_STATVFS:
- {
- LIBSSH2_SFTP_STATVFS statvfs;
- rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)),
- &statvfs);
-
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- failf(data, "statvfs command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- else if(rc == 0) {
-#ifdef _MSC_VER
-#define CURL_LIBSSH2_VFS_SIZE_MASK "I64u"
-#else
-#define CURL_LIBSSH2_VFS_SIZE_MASK "llu"
-#endif
- char *tmp = aprintf("statvfs:\n"
- "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n",
- statvfs.f_bsize, statvfs.f_frsize,
- statvfs.f_blocks, statvfs.f_bfree,
- statvfs.f_bavail, statvfs.f_files,
- statvfs.f_ffree, statvfs.f_favail,
- statvfs.f_fsid, statvfs.f_flag,
- statvfs.f_namemax);
- if(!tmp) {
- result = CURLE_OUT_OF_MEMORY;
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- break;
- }
-
- result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
- free(tmp);
- if(result) {
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
- }
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_statvfs(data, sshc);
break;
- }
-#endif
+
case SSH_SFTP_GETINFO:
- {
if(data->set.get_filetime) {
- state(data, SSH_SFTP_FILETIME);
+ myssh_state(data, sshc, SSH_SFTP_FILETIME);
}
else {
- state(data, SSH_SFTP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
}
break;
- }
case SSH_SFTP_FILETIME:
{
LIBSSH2_SFTP_ATTRIBUTES attrs;
- rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
- curlx_uztoui(strlen(sshp->path)),
- LIBSSH2_SFTP_STAT, &attrs);
+ int rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ LIBSSH2_SFTP_STAT, &attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc == 0) {
data->info.filetime = (time_t)attrs.mtime;
}
- state(data, SSH_SFTP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
break;
}
case SSH_SFTP_TRANS_INIT:
if(data->state.upload)
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
else {
if(sshp->path[strlen(sshp->path)-1] == '/')
- state(data, SSH_SFTP_READDIR_INIT);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_INIT);
else
- state(data, SSH_SFTP_DOWNLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
}
break;
case SSH_SFTP_UPLOAD_INIT:
result = sftp_upload_init(data, sshc, sshp, block);
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
}
break;
case SSH_SFTP_CREATE_DIRS_INIT:
if(strlen(sshp->path) > 1) {
sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
- state(data, SSH_SFTP_CREATE_DIRS);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
}
else {
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
}
break;
@@ -2415,401 +2891,121 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
*sshc->slash_pos = 0;
infof(data, "Creating directory '%s'", sshp->path);
- state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
break;
}
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
break;
case SSH_SFTP_CREATE_DIRS_MKDIR:
- /* 'mode' - parameter is preliminary - default to 0644 */
- rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path,
- curlx_uztoui(strlen(sshp->path)),
- (long)data->set.new_directory_perms);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- *sshc->slash_pos = '/';
- ++sshc->slash_pos;
- if(rc < 0) {
- /*
- * Abort if failure was not that the dir already exists or the
- * permission was denied (creation might succeed further down the
- * path) - retry on unspecific FAILURE also
- */
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
- (sftperr != LIBSSH2_FX_FAILURE) &&
- (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) {
- result = sftp_libssh2_error_to_CURLE(sftperr);
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = result ? result : CURLE_SSH;
- break;
- }
- rc = 0; /* clear rc and continue */
- }
- state(data, SSH_SFTP_CREATE_DIRS);
+ result = ssh_state_sftp_create_dirs_mkdir(data, sshc, sshp);
break;
case SSH_SFTP_READDIR_INIT:
- Curl_pgrsSetDownloadSize(data, -1);
- if(data->req.no_body) {
- state(data, SSH_STOP);
- break;
- }
-
- /*
- * This is a directory that we are trying to get, so produce a directory
- * listing
- */
- sshc->sftp_handle =
- libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
- curlx_uztoui(strlen(sshp->path)),
- 0, 0, LIBSSH2_SFTP_OPENDIR);
- if(!sshc->sftp_handle) {
- unsigned long sftperr;
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
- sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- failf(data, "Could not open directory for reading: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- result = sftp_libssh2_error_to_CURLE(sftperr);
- sshc->actualcode = result ? result : CURLE_SSH;
- break;
- }
- Curl_dyn_init(&sshp->readdir, CURL_PATH_MAX * 2);
- state(data, SSH_SFTP_READDIR);
+ result = ssh_state_sftp_readdir_init(data, sshc, sshp);
break;
case SSH_SFTP_READDIR:
result = sftp_readdir(data, sshc, sshp, block);
if(result) {
- sshc->actualcode = result;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
}
break;
case SSH_SFTP_READDIR_LINK:
- rc =
- libssh2_sftp_symlink_ex(sshc->sftp_session,
- Curl_dyn_ptr(&sshp->readdir_link),
- (unsigned int)
- Curl_dyn_len(&sshp->readdir_link),
- sshp->readdir_filename,
- CURL_PATH_MAX, LIBSSH2_SFTP_READLINK);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- Curl_dyn_free(&sshp->readdir_link);
-
- /* append filename and extra output */
- result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename);
-
- if(result) {
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = result;
- break;
- }
-
- state(data, SSH_SFTP_READDIR_BOTTOM);
+ result = ssh_state_sftp_readdir_link(data, sshc, sshp);
break;
case SSH_SFTP_READDIR_BOTTOM:
- result = Curl_dyn_addn(&sshp->readdir, "\n", 1);
+ result = curlx_dyn_addn(&sshp->readdir, "\n", 1);
if(!result)
result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&sshp->readdir),
- Curl_dyn_len(&sshp->readdir));
+ curlx_dyn_ptr(&sshp->readdir),
+ curlx_dyn_len(&sshp->readdir));
if(result) {
- Curl_dyn_free(&sshp->readdir);
- state(data, SSH_STOP);
+ curlx_dyn_free(&sshp->readdir);
+ myssh_state(data, sshc, SSH_STOP);
}
else {
- Curl_dyn_reset(&sshp->readdir);
- state(data, SSH_SFTP_READDIR);
+ curlx_dyn_reset(&sshp->readdir);
+ myssh_state(data, sshc, SSH_SFTP_READDIR);
}
break;
case SSH_SFTP_READDIR_DONE:
- if(libssh2_sftp_closedir(sshc->sftp_handle) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
- sshc->sftp_handle = NULL;
+ if(libssh2_sftp_closedir(sshc->sftp_handle) == LIBSSH2_ERROR_EAGAIN)
+ result = CURLE_AGAIN;
+ else {
+ sshc->sftp_handle = NULL;
- /* no data to transfer */
- Curl_xfer_setup_nop(data);
- state(data, SSH_STOP);
+ /* no data to transfer */
+ Curl_xfer_setup_nop(data);
+ myssh_state(data, sshc, SSH_STOP);
+ }
break;
case SSH_SFTP_DOWNLOAD_INIT:
- /*
- * Work on getting the specified file
- */
- sshc->sftp_handle =
- libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
- curlx_uztoui(strlen(sshp->path)),
- LIBSSH2_FXF_READ, (long)data->set.new_file_perms,
- LIBSSH2_SFTP_OPENFILE);
- if(!sshc->sftp_handle) {
- unsigned long sftperr;
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
- sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- failf(data, "Could not open remote file for reading: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- result = sftp_libssh2_error_to_CURLE(sftperr);
- sshc->actualcode = result ? result : CURLE_SSH;
- break;
- }
- state(data, SSH_SFTP_DOWNLOAD_STAT);
+ result = ssh_state_sftp_download_init(data, sshc, sshp);
break;
case SSH_SFTP_DOWNLOAD_STAT:
result = sftp_download_stat(data, sshc, sshp, block);
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
}
break;
case SSH_SFTP_CLOSE:
- if(sshc->sftp_handle) {
- rc = libssh2_sftp_close(sshc->sftp_handle);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
- }
- sshc->sftp_handle = NULL;
- }
-
- Curl_safefree(sshp->path);
-
- DEBUGF(infof(data, "SFTP DONE done"));
-
- /* 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 &&
- sshc->nextstate != SSH_SFTP_CLOSE) {
- state(data, sshc->nextstate);
- sshc->nextstate = SSH_SFTP_CLOSE;
- }
- else {
- state(data, SSH_STOP);
- result = sshc->actualcode;
- }
+ result = ssh_state_sftp_close(data, sshc, sshp);
break;
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 */
-
- if(sshc->sftp_handle) {
- rc = libssh2_sftp_close(sshc->sftp_handle);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
- NULL, 0);
- infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
- }
- sshc->sftp_handle = NULL;
- }
- if(sshc->sftp_session) {
- rc = libssh2_sftp_shutdown(sshc->sftp_session);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- infof(data, "Failed to stop libssh2 sftp subsystem");
- }
- sshc->sftp_session = NULL;
- }
-
- Curl_safefree(sshc->homedir);
- data->state.most_recent_ftp_entrypath = NULL;
-
- state(data, SSH_SESSION_DISCONNECT);
+ result = ssh_state_sftp_shutdown(data, sshc);
break;
case SSH_SCP_TRANS_INIT:
result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
if(result) {
- sshc->actualcode = result;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
if(data->state.upload) {
if(data->state.infilesize < 0) {
failf(data, "SCP requires a known file size for upload");
- sshc->actualcode = CURLE_UPLOAD_FAILED;
- state(data, SSH_SCP_CHANNEL_FREE);
+ result = CURLE_UPLOAD_FAILED;
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
}
- state(data, SSH_SCP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT);
}
else {
- state(data, SSH_SCP_DOWNLOAD_INIT);
+ myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT);
}
break;
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, sshp->path, data->set.new_file_perms,
- data->state.infilesize);
- if(!sshc->ssh_channel) {
- int ssh_err;
- char *err_msg = NULL;
-
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
-
- ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0));
- failf(data, "%s", err_msg);
- state(data, SSH_SCP_CHANNEL_FREE);
- sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
- /* Map generic errors to upload failed */
- if(sshc->actualcode == CURLE_SSH ||
- sshc->actualcode == CURLE_REMOTE_FILE_NOT_FOUND)
- sshc->actualcode = CURLE_UPLOAD_FAILED;
- break;
- }
-
- /* upload data */
- data->req.size = data->state.infilesize;
- Curl_pgrsSetUploadSize(data, data->state.infilesize);
- Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
-
- /* not set by Curl_xfer_setup to preserve keepon bits */
- conn->sockfd = conn->writesockfd;
-
- if(result) {
- state(data, SSH_SCP_CHANNEL_FREE);
- sshc->actualcode = result;
- }
- else {
- /* store this original bitmask setup to use later on if we cannot
- figure out a "real" bitmask */
- sshc->orig_waitfor = data->req.keepon;
-
- /* 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 */
- data->state.select_bits = CURL_CSELECT_OUT;
-
- state(data, SSH_STOP);
- }
+ result = ssh_state_scp_upload_init(data, sshc, sshp);
break;
case SSH_SCP_DOWNLOAD_INIT:
- {
- curl_off_t bytecount;
-
- /*
- * We must check the remote file; if it is a directory no values will
- * be set in sb
- */
-
- /*
- * If support for >2GB files exists, use it.
- */
-
- /* get a fresh new channel from the ssh layer */
-#if LIBSSH2_VERSION_NUM < 0x010700
- struct stat sb;
- memset(&sb, 0, sizeof(struct stat));
- sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
- sshp->path, &sb);
-#else
- libssh2_struct_stat sb;
- memset(&sb, 0, sizeof(libssh2_struct_stat));
- sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session,
- sshp->path, &sb);
-#endif
-
- if(!sshc->ssh_channel) {
- int ssh_err;
- char *err_msg = NULL;
-
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
-
-
- ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0));
- failf(data, "%s", err_msg);
- state(data, SSH_SCP_CHANNEL_FREE);
- sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
- break;
- }
-
- /* download data */
- bytecount = (curl_off_t)sb.st_size;
- data->req.maxdownload = (curl_off_t)sb.st_size;
- Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE);
-
- /* not set by Curl_xfer_setup to preserve keepon bits */
- conn->writesockfd = conn->sockfd;
-
- /* 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 */
- data->state.select_bits = CURL_CSELECT_IN;
-
- if(result) {
- state(data, SSH_SCP_CHANNEL_FREE);
- sshc->actualcode = result;
- }
- else
- state(data, SSH_STOP);
- }
- break;
+ result = ssh_state_scp_download_init(data, sshc, sshp);
+ break;
case SSH_SCP_DONE:
if(data->state.upload)
- state(data, SSH_SCP_SEND_EOF);
+ myssh_state(data, sshc, SSH_SCP_SEND_EOF);
else
- state(data, SSH_SCP_CHANNEL_FREE);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_SEND_EOF:
if(sshc->ssh_channel) {
- rc = libssh2_channel_send_eof(sshc->ssh_channel);
+ int rc = libssh2_channel_send_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc) {
@@ -2820,13 +3016,14 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
rc, err_msg);
}
}
- state(data, SSH_SCP_WAIT_EOF);
+ myssh_state(data, sshc, SSH_SCP_WAIT_EOF);
break;
case SSH_SCP_WAIT_EOF:
if(sshc->ssh_channel) {
- rc = libssh2_channel_wait_eof(sshc->ssh_channel);
+ int rc = libssh2_channel_wait_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc) {
@@ -2836,13 +3033,14 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
infof(data, "Failed to get channel EOF: %d %s", rc, err_msg);
}
}
- state(data, SSH_SCP_WAIT_CLOSE);
+ myssh_state(data, sshc, SSH_SCP_WAIT_CLOSE);
break;
case SSH_SCP_WAIT_CLOSE:
if(sshc->ssh_channel) {
- rc = libssh2_channel_wait_closed(sshc->ssh_channel);
+ int rc = libssh2_channel_wait_closed(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc) {
@@ -2852,13 +3050,14 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
infof(data, "Channel failed to close: %d %s", rc, err_msg);
}
}
- state(data, SSH_SCP_CHANNEL_FREE);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_CHANNEL_FREE:
if(sshc->ssh_channel) {
- rc = libssh2_channel_free(sshc->ssh_channel);
+ int rc = libssh2_channel_free(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc < 0) {
@@ -2871,141 +3070,38 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
sshc->ssh_channel = NULL;
}
DEBUGF(infof(data, "SCP DONE phase complete"));
-#if 0 /* PREV */
- state(data, SSH_SESSION_DISCONNECT);
-#endif
- state(data, SSH_STOP);
- result = sshc->actualcode;
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_SESSION_DISCONNECT:
- /* during weird times when we have 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;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to free libssh2 scp subsystem: %d %s",
- rc, err_msg);
- }
- sshc->ssh_channel = NULL;
- }
-
- if(sshc->ssh_session) {
- rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to disconnect libssh2 session: %d %s",
- rc, err_msg);
- }
- }
-
- Curl_safefree(sshc->homedir);
- data->state.most_recent_ftp_entrypath = NULL;
-
- state(data, SSH_SESSION_FREE);
+ result = ssh_state_session_disconnect(data, sshc);
break;
case SSH_SESSION_FREE:
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
- if(sshc->kh) {
- libssh2_knownhost_free(sshc->kh);
- sshc->kh = NULL;
- }
-#endif
-
-#ifdef HAVE_LIBSSH2_AGENT_API
- if(sshc->ssh_agent) {
- rc = libssh2_agent_disconnect(sshc->ssh_agent);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to disconnect from libssh2 agent: %d %s",
- rc, err_msg);
- }
- libssh2_agent_free(sshc->ssh_agent);
- sshc->ssh_agent = NULL;
-
- /* NB: there is no need to free identities, they are part of internal
- agent stuff */
- sshc->sshagent_identity = NULL;
- sshc->sshagent_prev_identity = NULL;
- }
-#endif
-
- if(sshc->ssh_session) {
- rc = libssh2_session_free(sshc->ssh_session);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
- }
- sshc->ssh_session = NULL;
- }
-
- /* 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
-#ifdef HAVE_LIBSSH2_AGENT_API
- DEBUGASSERT(sshc->ssh_agent == 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);
-
+ result = sshc_cleanup(sshc, data, FALSE);
+ if(result)
+ break;
/* the code we are about to return */
- result = sshc->actualcode;
-
memset(sshc, 0, sizeof(struct ssh_conn));
-
connclose(conn, "SSH session free");
sshc->state = SSH_SESSION_FREE; /* current */
- sshc->nextstate = SSH_NO_STATE;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_QUIT:
default:
/* internal error */
- sshc->nextstate = SSH_NO_STATE;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
- } while(!rc && (sshc->state != SSH_STOP));
+ } while(!result && (sshc->state != SSH_STOP));
- if(rc == LIBSSH2_ERROR_EAGAIN) {
+ if(result == CURLE_AGAIN) {
/* we would block, we need to wait for the socket to be ready (in the
right direction too)! */
*block = TRUE;
+ result = CURLE_OK;
}
return result;
@@ -3038,10 +3134,11 @@ static int ssh_getsock(struct Curl_easy *data,
* function in all cases so that when it _does not_ return EAGAIN we can
* restore the default wait bits.
*/
-static void ssh_block2waitfor(struct Curl_easy *data, bool block)
+static void ssh_block2waitfor(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ bool block)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
int dir = 0;
if(block) {
dir = libssh2_session_block_directions(sshc->ssh_session);
@@ -3061,35 +3158,39 @@ static void ssh_block2waitfor(struct Curl_easy *data, bool block)
static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
CURLcode result = CURLE_OK;
bool block; /* we store the status and use that to provide a ssh_getsock()
implementation */
+ if(!sshc || !sshp)
+ return CURLE_FAILED_INIT;
+
do {
- result = ssh_statemachine(data, &block);
+ result = ssh_statemachine(data, sshc, sshp, &block);
*done = (sshc->state == SSH_STOP);
/* if there is no error, it is not done and it did not EWOULDBLOCK, then
try again */
} while(!result && !*done && !block);
- ssh_block2waitfor(data, block);
+ ssh_block2waitfor(data, sshc, block);
return result;
}
static CURLcode ssh_block_statemach(struct Curl_easy *data,
- struct connectdata *conn,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
bool disconnect)
{
- struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
- struct curltime dis = Curl_now();
+ struct curltime dis = curlx_now();
while((sshc->state != SSH_STOP) && !result) {
bool block;
timediff_t left = 1000;
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
- result = ssh_statemachine(data, &block);
+ result = ssh_statemachine(data, sshc, sshp, &block);
if(result)
break;
@@ -3107,7 +3208,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
return CURLE_OPERATION_TIMEDOUT;
}
}
- else if(Curl_timediff(now, dis) > 1000) {
+ else if(curlx_timediff(now, dis) > 1000) {
/* disconnect timeout */
failf(data, "Disconnect timed out");
result = CURLE_OK;
@@ -3116,7 +3217,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
if(block) {
int dir = libssh2_session_block_directions(sshc->ssh_session);
- curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ curl_socket_t sock = data->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)
@@ -3132,17 +3233,51 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
return result;
}
+static void myssh_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct SSHPROTO *sshp = entry;
+ (void)key;
+ (void)klen;
+ Curl_safefree(sshp->path);
+ curlx_dyn_free(&sshp->readdir);
+ curlx_dyn_free(&sshp->readdir_link);
+ free(sshp);
+}
+
+static void myssh_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct ssh_conn *sshc = entry;
+ (void)key;
+ (void)klen;
+ sshc_cleanup(sshc, NULL, TRUE);
+ free(sshc);
+}
+
/*
* SSH setup and connection
*/
static CURLcode ssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
- struct SSHPROTO *ssh;
+ struct ssh_conn *sshc;
+ struct SSHPROTO *sshp;
(void)conn;
- data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
- if(!ssh)
+ sshc = calloc(1, sizeof(*sshc));
+ if(!sshc)
+ return CURLE_OUT_OF_MEMORY;
+
+ sshc->initialised = TRUE;
+ if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
+
+ sshp = calloc(1, sizeof(*sshp));
+ if(!sshp)
+ return CURLE_OUT_OF_MEMORY;
+
+ curlx_dyn_init(&sshp->readdir, CURL_PATH_MAX * 2);
+ curlx_dyn_init(&sshp->readdir_link, CURL_PATH_MAX);
+ if(Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
@@ -3160,20 +3295,23 @@ static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer,
CURLcode result;
struct connectdata *conn = data->conn;
Curl_recv *backup = conn->recv[0];
- struct ssh_conn *ssh = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
int socknum = Curl_conn_sockindex(data, sock);
(void)flags;
+ if(!sshc)
+ return -1;
+
/* swap in the TLS reader function for this call only, and then swap back
the SSH one again */
- conn->recv[0] = ssh->tls_recv;
+ conn->recv[0] = sshc->tls_recv;
result = Curl_conn_recv(data, socknum, buffer, length, &nread);
conn->recv[0] = backup;
if(result == CURLE_AGAIN)
return -EAGAIN; /* magic return code for libssh2 */
else if(result)
return -1; /* generic error */
- Curl_debug(data, CURLINFO_DATA_IN, (char *)buffer, (size_t)nread);
+ Curl_debug(data, CURLINFO_DATA_IN, (const char *)buffer, (size_t)nread);
return nread;
}
@@ -3185,20 +3323,23 @@ static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer,
CURLcode result;
struct connectdata *conn = data->conn;
Curl_send *backup = conn->send[0];
- struct ssh_conn *ssh = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
int socknum = Curl_conn_sockindex(data, sock);
(void)flags;
+ if(!sshc)
+ return -1;
+
/* swap in the TLS writer function for this call only, and then swap back
the SSH one again */
- conn->send[0] = ssh->tls_send;
+ conn->send[0] = sshc->tls_send;
result = Curl_conn_send(data, socknum, buffer, length, FALSE, &nwrite);
conn->send[0] = backup;
if(result == CURLE_AGAIN)
return -EAGAIN; /* magic return code for libssh2 */
else if(result)
return -1; /* error */
- Curl_debug(data, CURLINFO_DATA_OUT, (char *)buffer, nwrite);
+ Curl_debug(data, CURLINFO_DATA_OUT, (const char *)buffer, nwrite);
return (ssize_t)nwrite;
}
#endif
@@ -3212,27 +3353,50 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
#ifdef CURL_LIBSSH2_DEBUG
curl_socket_t sock;
#endif
- struct ssh_conn *sshc;
- CURLcode result;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ CURLcode result;
- /* initialize per-handle data if not already */
- if(!data->req.p.ssh) {
- result = ssh_setup_connection(data, conn);
- if(result)
- return result;
+#if LIBSSH2_VERSION_NUM >= 0x010b00
+ {
+ const char *crypto_str;
+ switch(libssh2_crypto_engine()) {
+ case libssh2_gcrypt:
+ crypto_str = "libgcrypt";
+ break;
+ case libssh2_mbedtls:
+ crypto_str = "mbedTLS";
+ break;
+ case libssh2_openssl:
+ crypto_str = "openssl compatible";
+ break;
+ case libssh2_os400qc3:
+ crypto_str = "OS400QC3";
+ break;
+ case libssh2_wincng:
+ crypto_str = "WinCNG";
+ break;
+ default:
+ crypto_str = NULL;
+ break;
+ }
+ if(crypto_str)
+ infof(data, "libssh2 cryptography backend: %s", crypto_str);
}
+#endif
+
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* We default to persistent connections. We set this already in this connect
function to make the reuse checks properly be able to check this bit. */
connkeep(conn, "SSH default");
- sshc = &conn->proto.sshc;
-
+ if(conn->user)
+ infof(data, "User: '%s'", conn->user);
+ else
+ infof(data, "User: NULL");
#ifdef CURL_LIBSSH2_DEBUG
- if(conn->user) {
- infof(data, "User: %s", conn->user);
- }
if(conn->passwd) {
infof(data, "Password: %s", conn->passwd);
}
@@ -3323,14 +3487,11 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
conn->send[FIRSTSOCKET] = sftp_send;
}
- if(data->set.ssh_compression) {
-#if LIBSSH2_VERSION_NUM >= 0x010208
- if(libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0)
-#endif
- infof(data, "Failed to enable compression for ssh session");
+ if(data->set.ssh_compression &&
+ libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0) {
+ infof(data, "Failed to enable compression for ssh session");
}
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
int rc;
sshc->kh = libssh2_knownhost_init(sshc->ssh_session);
@@ -3348,14 +3509,13 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
infof(data, "Failed to read known hosts from %s",
data->set.str[STRING_SSH_KNOWNHOSTS]);
}
-#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
#ifdef CURL_LIBSSH2_DEBUG
libssh2_trace(sshc->ssh_session, ~0);
infof(data, "SSH socket: %d", (int)sock);
#endif /* CURL_LIBSSH2_DEBUG */
- state(data, SSH_INIT);
+ myssh_state(data, sshc, SSH_INIT);
result = ssh_multi_statemach(data, done);
@@ -3376,14 +3536,17 @@ CURLcode scp_perform(struct Curl_easy *data,
bool *connected,
bool *dophase_done)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
DEBUGF(infof(data, "DO phase starts"));
*dophase_done = FALSE; /* not done yet */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* start the first command in the DO phase */
- state(data, SSH_SCP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SCP_TRANS_INIT);
/* run the state-machine */
result = ssh_multi_statemach(data, dophase_done);
@@ -3420,13 +3583,13 @@ static CURLcode ssh_do(struct Curl_easy *data, bool *done)
CURLcode result;
bool connected = FALSE;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
*done = FALSE; /* default to false */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
data->req.size = -1; /* make sure this is unknown at this point */
-
- sshc->actualcode = CURLE_OK; /* reset error code */
sshc->secondCreateDirs = 0; /* reset the create dir attempt state
variable */
@@ -3443,6 +3606,110 @@ static CURLcode ssh_do(struct Curl_easy *data, bool *done)
return result;
}
+static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data,
+ bool block)
+{
+ int rc;
+
+ if(sshc->initialised) {
+ if(sshc->kh) {
+ libssh2_knownhost_free(sshc->kh);
+ sshc->kh = NULL;
+ }
+
+ if(sshc->ssh_agent) {
+ rc = libssh2_agent_disconnect(sshc->ssh_agent);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to disconnect from libssh2 agent: %d %s",
+ rc, err_msg);
+ }
+ libssh2_agent_free(sshc->ssh_agent);
+ sshc->ssh_agent = NULL;
+
+ /* NB: there is no need to free identities, they are part of internal
+ agent stuff */
+ sshc->sshagent_identity = NULL;
+ sshc->sshagent_prev_identity = NULL;
+ }
+
+ if(sshc->sftp_handle) {
+ rc = libssh2_sftp_close(sshc->sftp_handle);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
+ NULL, 0);
+ infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+ }
+ sshc->sftp_handle = NULL;
+ }
+
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_free(sshc->ssh_channel);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to free libssh2 scp subsystem: %d %s",
+ rc, err_msg);
+ }
+ sshc->ssh_channel = NULL;
+ }
+
+ if(sshc->sftp_session) {
+ rc = libssh2_sftp_shutdown(sshc->sftp_session);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data)
+ infof(data, "Failed to stop libssh2 sftp subsystem");
+ sshc->sftp_session = NULL;
+ }
+
+ if(sshc->ssh_session) {
+ rc = libssh2_session_free(sshc->ssh_session);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
+ }
+ sshc->ssh_session = NULL;
+ }
+
+ /* worst-case scenario cleanup */
+ DEBUGASSERT(sshc->ssh_session == NULL);
+ DEBUGASSERT(sshc->ssh_channel == NULL);
+ DEBUGASSERT(sshc->sftp_session == NULL);
+ DEBUGASSERT(sshc->sftp_handle == NULL);
+ DEBUGASSERT(sshc->kh == NULL);
+ DEBUGASSERT(sshc->ssh_agent == NULL);
+
+ Curl_safefree(sshc->rsa_pub);
+ Curl_safefree(sshc->rsa);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ Curl_safefree(sshc->homedir);
+ sshc->initialised = FALSE;
+ }
+ return CURLE_OK;
+}
+
+
/* 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 */
@@ -3451,15 +3718,18 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
bool dead_connection)
{
CURLcode result = CURLE_OK;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
(void) dead_connection;
- if(sshc->ssh_session) {
+ if(sshc && sshc->ssh_session && sshp) {
/* only if there is a session still around to use! */
- state(data, SSH_SESSION_DISCONNECT);
- result = ssh_block_statemach(data, conn, TRUE);
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
+ result = ssh_block_statemach(data, sshc, sshp, TRUE);
}
+ if(sshc)
+ return sshc_cleanup(sshc, data, TRUE);
return result;
}
@@ -3467,19 +3737,19 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
done functions */
static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
CURLcode result = CURLE_OK;
- struct SSHPROTO *sshp = data->req.p.ssh;
- struct connectdata *conn = data->conn;
+
+ if(!sshc || !sshp)
+ return CURLE_FAILED_INIT;
if(!status)
/* run the state-machine */
- result = ssh_block_statemach(data, conn, FALSE);
+ result = ssh_block_statemach(data, sshc, sshp, FALSE);
else
result = status;
- Curl_safefree(sshp->path);
- Curl_dyn_free(&sshp->readdir);
-
if(Curl_pgrsDone(data))
return CURLE_ABORTED_BY_CALLBACK;
@@ -3491,13 +3761,13 @@ static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
(void)premature; /* not used */
- if(!status)
- state(data, SSH_SCP_DONE);
+ if(sshc && !status)
+ myssh_state(data, sshc, SSH_SCP_DONE);
return ssh_done(data, status);
-
}
static ssize_t scp_send(struct Curl_easy *data, int sockindex,
@@ -3505,14 +3775,18 @@ static ssize_t scp_send(struct Curl_easy *data, int sockindex,
{
ssize_t nwrite;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex; /* we only support SCP on the fixed known primary socket */
(void)eos;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
/* libssh2_channel_write() returns int! */
nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len);
- ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN));
+ ssh_block2waitfor(data, sshc, (nwrite == LIBSSH2_ERROR_EAGAIN));
if(nwrite == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
@@ -3531,13 +3805,17 @@ static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
{
ssize_t nread;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex; /* we only support SCP on the fixed known primary socket */
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
/* libssh2_channel_read() returns int */
nread = (ssize_t) libssh2_channel_read(sshc->ssh_channel, mem, len);
- ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN));
+ ssh_block2waitfor(data, sshc, (nread == LIBSSH2_ERROR_EAGAIN));
if(nread == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
nread = -1;
@@ -3564,14 +3842,17 @@ CURLcode sftp_perform(struct Curl_easy *data,
bool *connected,
bool *dophase_done)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
DEBUGF(infof(data, "DO phase starts"));
*dophase_done = FALSE; /* not done yet */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* start the first command in the DO phase */
- state(data, SSH_SFTP_QUOTE_INIT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_INIT);
/* run the state-machine */
result = ssh_multi_statemach(data, dophase_done);
@@ -3604,18 +3885,21 @@ static CURLcode sftp_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead_connection)
{
CURLcode result = CURLE_OK;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
(void) dead_connection;
DEBUGF(infof(data, "SSH DISCONNECT starts now"));
- if(sshc->ssh_session) {
+ if(sshc && sshc->ssh_session && sshp) {
/* only if there is a session still around to use! */
- state(data, SSH_SFTP_SHUTDOWN);
- result = ssh_block_statemach(data, conn, TRUE);
+ myssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
+ result = ssh_block_statemach(data, sshc, sshp, TRUE);
}
DEBUGF(infof(data, "SSH DISCONNECT is done"));
+ if(sshc)
+ sshc_cleanup(sshc, data, TRUE);
return result;
@@ -3625,7 +3909,10 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+
+ if(!sshc)
+ return CURLE_FAILED_INIT;
if(!status) {
/* Post quote commands are executed after the SFTP_CLOSE state to avoid
@@ -3633,7 +3920,7 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
operation */
if(!premature && data->set.postquote && !conn->bits.retry)
sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
}
return ssh_done(data, status);
}
@@ -3644,13 +3931,17 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
{
ssize_t nwrite;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex;
(void)eos;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len);
- ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN));
+ ssh_block2waitfor(data, sshc, (nwrite == LIBSSH2_ERROR_EAGAIN));
if(nwrite == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
@@ -3673,12 +3964,16 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
{
ssize_t nread;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
nread = libssh2_sftp_read(sshc->sftp_handle, mem, len);
- ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN));
+ ssh_block2waitfor(data, sshc, (nread == LIBSSH2_ERROR_EAGAIN));
if(nread == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
@@ -3759,25 +4054,21 @@ static const char *sftp_libssh2_strerror(unsigned long err)
CURLcode Curl_ssh_init(void)
{
-#ifdef HAVE_LIBSSH2_INIT
if(libssh2_init(0)) {
DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n"));
return CURLE_FAILED_INIT;
}
-#endif
return CURLE_OK;
}
void Curl_ssh_cleanup(void)
{
-#ifdef HAVE_LIBSSH2_EXIT
(void)libssh2_exit();
-#endif
}
void Curl_ssh_version(char *buffer, size_t buflen)
{
- (void)msnprintf(buffer, buflen, "libssh2/%s", CURL_LIBSSH2_VERSION);
+ (void)msnprintf(buffer, buflen, "libssh2/%s", libssh2_version(0));
}
/* The SSH session is associated with the *CONNECTION* but the callback user
@@ -3789,8 +4080,8 @@ static void ssh_attach(struct Curl_easy *data, struct connectdata *conn)
DEBUGASSERT(data);
DEBUGASSERT(conn);
if(conn->handler->protocol & PROTO_FAMILY_SSH) {
- struct ssh_conn *sshc = &conn->proto.sshc;
- if(sshc->ssh_session) {
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ if(sshc && sshc->ssh_session) {
/* only re-attach if the session already exists */
void **abstract = libssh2_session_abstract(sshc->ssh_session);
*abstract = data;
diff --git a/libs/libcurl/src/vssh/ssh.h b/libs/libcurl/src/vssh/ssh.h
index 192c1d7970..73aec97a97 100644
--- a/libs/libcurl/src/vssh/ssh.h
+++ b/libs/libcurl/src/vssh/ssh.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_LIBSSH2)
#include <libssh2.h>
@@ -41,6 +41,11 @@
#include "curl_path.h"
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_SSH_EASY "meta:proto:ssh:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_SSH_CONN "meta:proto:ssh:conn"
+
/****************************************************************************
* SSH unique setup
***************************************************************************/
@@ -141,12 +146,8 @@ struct ssh_conn {
const char *passphrase; /* pass-phrase to use */
char *rsa_pub; /* strdup'ed public key file */
char *rsa; /* strdup'ed private key file */
- bool authed; /* the connection has been authenticated fine */
- bool acceptfail; /* used by the SFTP_QUOTE (continue if
- quote command fails) */
sshstate state; /* always use ssh.c:state() to change state! */
sshstate nextstate; /* the state to goto after stopping */
- CURLcode actualcode; /* the actual error code */
struct curl_slist *quote_item; /* for the quote option */
char *quote_path1; /* two generic pointers for the QUOTE stuff */
char *quote_path2;
@@ -162,6 +163,7 @@ struct ssh_conn {
char *slash_pos; /* used by the SFTP_CREATE_DIRS state */
#if defined(USE_LIBSSH)
+ CURLcode actualcode; /* the actual error code */
char *readdir_linkPath;
size_t readdir_len;
struct dynbuf readdir_buf;
@@ -177,6 +179,10 @@ struct ssh_conn {
sftp_dir sftp_dir;
unsigned sftp_recv_state; /* 0 or 1 */
+#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
+ sftp_aio sftp_aio;
+ unsigned sftp_send_state; /* 0 or 1 */
+#endif
int sftp_file_index; /* for async read */
sftp_attributes readdir_attrs; /* used by the SFTP readdir actions */
sftp_attributes readdir_link_attrs; /* used by the SFTP readdir actions */
@@ -197,63 +203,38 @@ struct ssh_conn {
Curl_send *tls_send;
#endif
-#ifdef HAVE_LIBSSH2_AGENT_API
LIBSSH2_AGENT *ssh_agent; /* proxy to ssh-agent/pageant */
- struct libssh2_agent_publickey *sshagent_identity,
- *sshagent_prev_identity;
-#endif
-
- /* note that HAVE_LIBSSH2_KNOWNHOST_API is a define set in the libssh2.h
- header */
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+ struct libssh2_agent_publickey *sshagent_identity;
+ struct libssh2_agent_publickey *sshagent_prev_identity;
LIBSSH2_KNOWNHOSTS *kh;
-#endif
#elif defined(USE_WOLFSSH)
+ CURLcode actualcode; /* the actual error code */
WOLFSSH *ssh_session;
WOLFSSH_CTX *ctx;
word32 handleSz;
byte handle[WOLFSSH_MAX_HANDLE];
curl_off_t offset;
#endif /* USE_LIBSSH */
+ BIT(initialised);
+ BIT(authed); /* the connection has been authenticated fine */
+ BIT(acceptfail); /* used by the SFTP_QUOTE (continue if
+ quote command fails) */
};
-#if defined(USE_LIBSSH2)
-
-/* Feature detection based on version numbers to better work with
- non-configure platforms */
-
-#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x001000)
-# error "SCP/SFTP protocols require libssh2 0.16 or later"
-#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010000
-#define HAVE_LIBSSH2_SFTP_SEEK64 1
+#ifdef USE_LIBSSH
+#if LIBSSH_VERSION_INT < SSH_VERSION_INT(0, 9, 0)
+# error "SCP/SFTP protocols require libssh 0.9.0 or later"
#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010100
-#define HAVE_LIBSSH2_VERSION 1
-#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010205
-#define HAVE_LIBSSH2_INIT 1
-#define HAVE_LIBSSH2_EXIT 1
#endif
-#if LIBSSH2_VERSION_NUM >= 0x010206
-#define HAVE_LIBSSH2_KNOWNHOST_CHECKP 1
-#define HAVE_LIBSSH2_SCP_SEND64 1
-#endif
+#if defined(USE_LIBSSH2)
-#if LIBSSH2_VERSION_NUM >= 0x010208
-#define HAVE_LIBSSH2_SESSION_HANDSHAKE 1
-#endif
+/* Feature detection based on version numbers to better work with
+ non-configure platforms */
-#ifdef HAVE_LIBSSH2_VERSION
-/* get it runtime if possible */
-#define CURL_LIBSSH2_VERSION libssh2_version(0)
-#else
-/* use build-time if runtime not possible */
-#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION
+#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010208)
+# error "SCP/SFTP protocols require libssh2 1.2.8 or later"
+/* 1.2.8 was released on April 5 2011 */
#endif
#endif /* USE_LIBSSH2 */
diff --git a/libs/libcurl/src/vssh/wolfssh.c b/libs/libcurl/src/vssh/wolfssh.c
index 377aa99bcc..7bf3e8a90b 100644
--- a/libs/libcurl/src/vssh/wolfssh.c
+++ b/libs/libcurl/src/vssh/wolfssh.c
@@ -22,30 +22,30 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_WOLFSSH
#include <limits.h>
-#include "urldata.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "sendf.h"
-#include "progress.h"
+#include "../urldata.h"
+#include "../url.h"
+#include "../cfilters.h"
+#include "../connect.h"
+#include "../sendf.h"
+#include "../progress.h"
#include "curl_path.h"
-#include "strtoofft.h"
-#include "transfer.h"
-#include "speedcheck.h"
-#include "select.h"
-#include "multiif.h"
-#include "warnless.h"
-#include "strdup.h"
+#include "../transfer.h"
+#include "../speedcheck.h"
+#include "../select.h"
+#include "../multiif.h"
+#include "../curlx/warnless.h"
+#include "../strdup.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
static CURLcode wssh_connect(struct Curl_easy *data, bool *done);
static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done);
@@ -71,6 +71,7 @@ static int wssh_getsock(struct Curl_easy *data,
curl_socket_t *sock);
static CURLcode wssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
+static void wssh_sshc_cleanup(struct ssh_conn *sshc);
#if 0
/*
@@ -95,6 +96,7 @@ const struct Curl_handler Curl_handler_scp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SCP, /* protocol */
PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
@@ -125,6 +127,7 @@ const struct Curl_handler Curl_handler_sftp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */
@@ -136,10 +139,10 @@ const struct Curl_handler Curl_handler_sftp = {
* SSH State machine related code
*/
/* This is the ONLY way to change SSH state! */
-static void state(struct Curl_easy *data, sshstate nowstate)
+static void wssh_state(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ sshstate nowstate)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char * const names[] = {
@@ -206,14 +209,14 @@ static void state(struct Curl_easy *data, sshstate nowstate)
};
/* a precaution to make sure the lists are in sync */
- DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
+ DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST);
if(sshc->state != nowstate) {
infof(data, "wolfssh %p state change from %s to %s",
(void *)sshc, names[sshc->state], names[nowstate]);
}
#endif
-
+ (void)data;
sshc->state = nowstate;
}
@@ -250,19 +253,23 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
const void *mem, size_t len, bool eos, CURLcode *err)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
word32 offset[2];
int rc;
(void)sockindex;
(void)eos;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
offset[0] = (word32)sshc->offset & 0xFFFFFFFF;
offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF;
rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle,
sshc->handleSz,
&offset[0],
- (byte *)mem, (word32)len);
+ (byte *)CURL_UNCONST(mem), (word32)len);
if(rc == WS_FATAL_ERROR)
rc = wolfSSH_get_error(sshc->ssh_session);
@@ -296,10 +303,14 @@ static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
{
int rc;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
word32 offset[2];
(void)sockindex;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
offset[0] = (word32)sshc->offset & 0xFFFFFFFF;
offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF;
@@ -331,17 +342,45 @@ static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
return (ssize_t)rc;
}
+static void wssh_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct SSHPROTO *sshp = entry;
+ (void)key;
+ (void)klen;
+ Curl_safefree(sshp->path);
+ free(sshp);
+}
+
+static void wssh_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct ssh_conn *sshc = entry;
+ (void)key;
+ (void)klen;
+ wssh_sshc_cleanup(sshc);
+ free(sshc);
+}
+
/*
* SSH setup and connection
*/
static CURLcode wssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
- struct SSHPROTO *ssh;
+ struct ssh_conn *sshc;
+ struct SSHPROTO *sshp;
(void)conn;
- data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
- if(!ssh)
+ sshc = calloc(1, sizeof(*sshc));
+ if(!sshc)
+ return CURLE_OUT_OF_MEMORY;
+
+ sshc->initialised = TRUE;
+ if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, wssh_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
+
+ sshp = calloc(1, sizeof(*sshp));
+ if(!sshp ||
+ Curl_meta_set(data, CURL_META_SSH_EASY, sshp, wssh_easy_dtor))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
@@ -366,13 +405,13 @@ static int userauth(byte authtype,
static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc;
- /* initialize per-handle data if not already */
- if(!data->req.p.ssh)
- wssh_setup_connection(data, conn);
+ if(!sshc || !sshp)
+ return CURLE_FAILED_INIT;
/* We default to persistent connections. We set this already in this connect
function to make the reuse checks properly be able to check this bit. */
@@ -386,7 +425,6 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
conn->recv[FIRSTSOCKET] = wsftp_recv;
conn->send[FIRSTSOCKET] = wsftp_send;
}
- sshc = &conn->proto.sshc;
sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
if(!sshc->ctx) {
failf(data, "No wolfSSH context");
@@ -421,14 +459,13 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
*done = TRUE;
if(conn->handler->protocol & CURLPROTO_SCP)
- state(data, SSH_INIT);
+ wssh_state(data, sshc, SSH_INIT);
else
- state(data, SSH_SFTP_INIT);
+ wssh_state(data, sshc, SSH_SFTP_INIT);
return wssh_multi_statemach(data, done);
error:
- wolfSSH_free(sshc->ssh_session);
- wolfSSH_CTX_free(sshc->ctx);
+ wssh_sshc_cleanup(sshc);
return CURLE_FAILED_INIT;
}
@@ -439,20 +476,24 @@ error:
* wants to be called again when the socket is ready
*/
-static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
+static CURLcode wssh_statemach_act(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ bool *block)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
- struct SSHPROTO *sftp_scp = data->req.p.ssh;
+ struct SSHPROTO *sftp_scp = Curl_meta_get(data, CURL_META_SSH_EASY);
WS_SFTPNAME *name;
int rc = 0;
*block = FALSE; /* we are not blocking by default */
+ if(!sftp_scp)
+ return CURLE_FAILED_INIT;
+
do {
switch(sshc->state) {
case SSH_INIT:
- state(data, SSH_S_STARTUP);
+ wssh_state(data, sshc, SSH_S_STARTUP);
break;
case SSH_S_STARTUP:
@@ -470,11 +511,11 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
return CURLE_OK;
}
else if(rc != WS_SUCCESS) {
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
return CURLE_SSH;
}
infof(data, "wolfssh connected");
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
break;
case SSH_STOP:
break;
@@ -495,7 +536,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
}
else if(rc == WS_SUCCESS) {
infof(data, "wolfssh SFTP connected");
- state(data, SSH_SFTP_REALPATH);
+ wssh_state(data, sshc, SSH_SFTP_REALPATH);
}
else {
failf(data, "wolfssh SFTP connect error %d", rc);
@@ -503,7 +544,8 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
}
break;
case SSH_SFTP_REALPATH:
- name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
+ name = wolfSSH_SFTP_RealPath(sshc->ssh_session,
+ (char *)CURL_UNCONST("."));
rc = wolfSSH_get_error(sshc->ssh_session);
if(rc == WS_WANT_READ) {
*block = TRUE;
@@ -520,7 +562,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
if(!sshc->homedir)
sshc->actualcode = CURLE_OUT_OF_MEMORY;
wolfSSH_SFTPNAME_list_free(name);
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
failf(data, "wolfssh SFTP realpath %d", rc);
@@ -530,35 +572,35 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path);
if(result) {
sshc->actualcode = result;
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
break;
}
if(data->set.quote) {
infof(data, "Sending quote commands");
sshc->quote_item = data->set.quote;
- state(data, SSH_SFTP_QUOTE);
+ wssh_state(data, sshc, SSH_SFTP_QUOTE);
}
else {
- state(data, SSH_SFTP_GETINFO);
+ wssh_state(data, sshc, SSH_SFTP_GETINFO);
}
break;
case SSH_SFTP_GETINFO:
if(data->set.get_filetime) {
- state(data, SSH_SFTP_FILETIME);
+ wssh_state(data, sshc, SSH_SFTP_FILETIME);
}
else {
- state(data, SSH_SFTP_TRANS_INIT);
+ wssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
}
break;
case SSH_SFTP_TRANS_INIT:
if(data->state.upload)
- state(data, SSH_SFTP_UPLOAD_INIT);
+ wssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
else {
if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
- state(data, SSH_SFTP_READDIR_INIT);
+ wssh_state(data, sshc, SSH_SFTP_READDIR_INIT);
else
- state(data, SSH_SFTP_DOWNLOAD_INIT);
+ wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
}
break;
case SSH_SFTP_UPLOAD_INIT: {
@@ -621,7 +663,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
failf(data, "wolfssh SFTP upload open failed: %d", rc);
return CURLE_SSH;
}
- state(data, SSH_SFTP_DOWNLOAD_STAT);
+ wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
/* If we have a restart point then we need to seek to the correct
position. */
@@ -687,7 +729,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
conn->sockfd = conn->writesockfd;
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ wssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
@@ -705,7 +747,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
timeout here */
Curl_expire(data, 0, EXPIRE_RUN_NOW);
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
}
break;
}
@@ -728,7 +770,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
}
else if(rc == WS_SUCCESS) {
infof(data, "wolfssh SFTP open succeeded");
- state(data, SSH_SFTP_DOWNLOAD_STAT);
+ wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
return CURLE_OK;
}
@@ -783,7 +825,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
/* no data to transfer */
Curl_xfer_setup_nop(data);
infof(data, "File already completely downloaded");
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
break;
}
Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
@@ -799,20 +841,24 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
if(result) {
/* this should never occur; the close state should be entered
at the time the error occurs */
- state(data, SSH_SFTP_CLOSE);
+ wssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
}
break;
}
case SSH_SFTP_CLOSE:
- if(sshc->handleSz)
+ if(sshc->handleSz) {
rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle,
sshc->handleSz);
- else
+ if(rc != WS_SUCCESS)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ }
+ else {
rc = WS_SUCCESS; /* directory listing */
+ }
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
@@ -824,7 +870,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
return CURLE_OK;
}
else if(rc == WS_SUCCESS) {
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
@@ -834,10 +880,10 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
case SSH_SFTP_READDIR_INIT:
Curl_pgrsSetDownloadSize(data, -1);
if(data->req.no_body) {
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
break;
}
- state(data, SSH_SFTP_READDIR);
+ wssh_state(data, sshc, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR:
@@ -865,7 +911,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
data->set.list_only ?
name->fName : name->lName);
if(!line) {
- state(data, SSH_SFTP_CLOSE);
+ wssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
@@ -879,17 +925,15 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
name = name->next;
}
wolfSSH_SFTPNAME_list_free(origname);
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
return result;
}
failf(data, "wolfssh SFTP ls failed: %d", rc);
return CURLE_SSH;
case SSH_SFTP_SHUTDOWN:
- Curl_safefree(sshc->homedir);
- wolfSSH_free(sshc->ssh_session);
- wolfSSH_CTX_free(sshc->ctx);
- state(data, SSH_STOP);
+ wssh_sshc_cleanup(sshc);
+ wssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
default:
break;
@@ -902,12 +946,15 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
bool block; /* we store the status and use that to provide a ssh_getsock()
implementation */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
+
do {
- result = wssh_statemach_act(data, &block);
+ result = wssh_statemach_act(data, sshc, &block);
*done = (sshc->state == SSH_STOP);
/* if there is no error, it is not done and it did not EWOULDBLOCK, then
try again */
@@ -932,6 +979,7 @@ CURLcode wscp_perform(struct Curl_easy *data,
static
CURLcode wsftp_perform(struct Curl_easy *data,
+ struct ssh_conn *sshc,
bool *connected,
bool *dophase_done)
{
@@ -942,7 +990,7 @@ CURLcode wsftp_perform(struct Curl_easy *data,
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
- state(data, SSH_SFTP_QUOTE_INIT);
+ wssh_state(data, sshc, SSH_SFTP_QUOTE_INIT);
/* run the state-machine */
result = wssh_multi_statemach(data, dophase_done);
@@ -964,9 +1012,12 @@ static CURLcode wssh_do(struct Curl_easy *data, bool *done)
CURLcode result;
bool connected = FALSE;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
*done = FALSE; /* default to false */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
+
data->req.size = -1; /* make sure this is unknown at this point */
sshc->actualcode = CURLE_OK; /* reset error code */
sshc->secondCreateDirs = 0; /* reset the create dir attempt state
@@ -980,24 +1031,24 @@ static CURLcode wssh_do(struct Curl_easy *data, bool *done)
if(conn->handler->protocol & CURLPROTO_SCP)
result = wscp_perform(data, &connected, done);
else
- result = wsftp_perform(data, &connected, done);
+ result = wsftp_perform(data, sshc, &connected, done);
return result;
}
static CURLcode wssh_block_statemach(struct Curl_easy *data,
- bool disconnect)
+ struct ssh_conn *sshc,
+ bool disconnect)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
while((sshc->state != SSH_STOP) && !result) {
bool block;
timediff_t left = 1000;
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
- result = wssh_statemach_act(data, &block);
+ result = wssh_statemach_act(data, sshc, &block);
if(result)
break;
@@ -1037,20 +1088,19 @@ static CURLcode wssh_block_statemach(struct Curl_easy *data,
/* generic done function for both SCP and SFTP called from their specific
done functions */
-static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
+static CURLcode wssh_done(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ CURLcode status)
{
CURLcode result = CURLE_OK;
- struct SSHPROTO *sftp_scp = data->req.p.ssh;
if(!status) {
/* run the state-machine */
- result = wssh_block_statemach(data, FALSE);
+ result = wssh_block_statemach(data, sshc, FALSE);
}
else
result = status;
- if(sftp_scp)
- Curl_safefree(sftp_scp->path);
if(Curl_pgrsDone(data))
return CURLE_ABORTED_BY_CALLBACK;
@@ -1058,6 +1108,19 @@ static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
return result;
}
+static void wssh_sshc_cleanup(struct ssh_conn *sshc)
+{
+ if(sshc->ssh_session) {
+ wolfSSH_free(sshc->ssh_session);
+ sshc->ssh_session = NULL;
+ }
+ if(sshc->ctx) {
+ wolfSSH_CTX_free(sshc->ctx);
+ sshc->ctx = NULL;
+ }
+ Curl_safefree(sshc->homedir);
+}
+
#if 0
static CURLcode wscp_done(struct Curl_easy *data,
CURLcode code, bool premature)
@@ -1083,11 +1146,11 @@ static CURLcode wscp_doing(struct Curl_easy *data,
static CURLcode wscp_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead_connection)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
- (void)data;
- (void)conn;
(void)dead_connection;
-
+ if(sshc)
+ wssh_sshc_cleanup(sshc);
return result;
}
#endif
@@ -1095,10 +1158,14 @@ static CURLcode wscp_disconnect(struct Curl_easy *data,
static CURLcode wsftp_done(struct Curl_easy *data,
CURLcode code, bool premature)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
(void)premature;
- state(data, SSH_SFTP_CLOSE);
+ if(!sshc)
+ return CURLE_FAILED_INIT;
+
+ wssh_state(data, sshc, SSH_SFTP_CLOSE);
- return wssh_done(data, code);
+ return wssh_done(data, sshc, code);
}
static CURLcode wsftp_doing(struct Curl_easy *data,
@@ -1116,17 +1183,20 @@ static CURLcode wsftp_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
(void)dead;
DEBUGF(infof(data, "SSH DISCONNECT starts now"));
- if(conn->proto.sshc.ssh_session) {
+ if(sshc && sshc->ssh_session) {
/* only if there is a session still around to use! */
- state(data, SSH_SFTP_SHUTDOWN);
- result = wssh_block_statemach(data, TRUE);
+ wssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
+ result = wssh_block_statemach(data, sshc, TRUE);
}
+ if(sshc)
+ wssh_sshc_cleanup(sshc);
DEBUGF(infof(data, "SSH DISCONNECT is done"));
return result;
}