#define SSH_S_IFLNK 0120000
#endif
-/* Local functions: */
-static CURLcode myssh_connect(struct Curl_easy *data, bool *done);
-static CURLcode myssh_multi_statemach(struct Curl_easy *data,
- bool *done);
-static CURLcode myssh_do_it(struct Curl_easy *data, bool *done);
-
-static CURLcode scp_done(struct Curl_easy *data,
- CURLcode, bool premature);
-static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done);
-static CURLcode scp_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection);
-
-static CURLcode sftp_done(struct Curl_easy *data,
- CURLcode, bool premature);
-static CURLcode sftp_doing(struct Curl_easy *data,
- bool *dophase_done);
-static CURLcode sftp_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead);
-static CURLcode sftp_perform(struct Curl_easy *data,
- bool *connected,
- bool *dophase_done);
-
-static CURLcode myssh_pollset(struct Curl_easy *data,
- struct easy_pollset *ps);
-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.
- */
-
-const struct Curl_handler Curl_handler_scp = {
- "SCP", /* scheme */
- myssh_setup_connection, /* setup_connection */
- myssh_do_it, /* do_it */
- scp_done, /* done */
- ZERO_NULL, /* do_more */
- myssh_connect, /* connect_it */
- myssh_multi_statemach, /* connecting */
- scp_doing, /* doing */
- myssh_pollset, /* proto_pollset */
- myssh_pollset, /* doing_pollset */
- ZERO_NULL, /* domore_pollset */
- myssh_pollset, /* perform_pollset */
- scp_disconnect, /* disconnect */
- ZERO_NULL, /* write_resp */
- 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 */
- PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
- PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE
-};
-
-/*
- * SFTP protocol handler.
- */
-
-const struct Curl_handler Curl_handler_sftp = {
- "SFTP", /* scheme */
- myssh_setup_connection, /* setup_connection */
- myssh_do_it, /* do_it */
- sftp_done, /* done */
- ZERO_NULL, /* do_more */
- myssh_connect, /* connect_it */
- myssh_multi_statemach, /* connecting */
- sftp_doing, /* doing */
- myssh_pollset, /* proto_pollset */
- myssh_pollset, /* doing_pollset */
- ZERO_NULL, /* domore_pollset */
- myssh_pollset, /* perform_pollset */
- sftp_disconnect, /* disconnect */
- ZERO_NULL, /* write_resp */
- 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 */
- PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
- PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE
-};
-
static CURLcode sftp_error_to_CURLE(int err)
{
switch(err) {
myssh_to(data, sshc, SSH_S_STARTUP);
}
+static void myssh_block2waitfor(struct connectdata *conn,
+ struct ssh_conn *sshc,
+ bool block)
+{
+ (void)conn;
+ if(block) {
+ int dir = ssh_get_poll_flags(sshc->ssh_session);
+ /* translate the libssh define bits into our own bit defines */
+ sshc->waitfor =
+ ((dir & SSH_READ_PENDING) ? KEEP_RECV : 0) |
+ ((dir & SSH_WRITE_PENDING) ? KEEP_SEND : 0);
+ }
+ else
+ sshc->waitfor = 0;
+}
+
static int myssh_in_S_STARTUP(struct Curl_easy *data,
struct ssh_conn *sshc)
{
return rc;
}
+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;
+ }
+}
+
/*
* 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
return CURLE_OK;
}
-static void myssh_block2waitfor(struct connectdata *conn,
- struct ssh_conn *sshc,
- bool block)
-{
- (void)conn;
- if(block) {
- int dir = ssh_get_poll_flags(sshc->ssh_session);
- /* translate the libssh define bits into our own bit defines */
- sshc->waitfor =
- ((dir & SSH_READ_PENDING) ? KEEP_RECV : 0) |
- ((dir & SSH_WRITE_PENDING) ? KEEP_SEND : 0);
- }
- else
- sshc->waitfor = 0;
-}
-
/* called repeatedly until done from multi.c */
static CURLcode myssh_multi_statemach(struct Curl_easy *data,
bool *done)
return result;
}
-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 = 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 directory attempt state
- variable */
-
- Curl_pgrsReset(data);
-
- if(conn->handler->protocol & CURLPROTO_SCP)
- result = scp_perform(data, &connected, done);
- else
- result = sftp_perform(data, &connected, 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 */
}
}
+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 = 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 directory attempt state
+ variable */
+
+ Curl_pgrsReset(data);
+
+ if(conn->handler->protocol & CURLPROTO_SCP)
+ result = scp_perform(data, &connected, done);
+ else
+ result = sftp_perform(data, &connected, done);
+
+ return result;
+}
+
CURLcode Curl_ssh_init(void)
{
if(ssh_init()) {
(void)curl_msnprintf(buffer, buflen, "libssh/%s", ssh_version(0));
}
+/*
+ * SCP protocol handler.
+ */
+const struct Curl_handler Curl_handler_scp = {
+ "SCP", /* scheme */
+ myssh_setup_connection, /* setup_connection */
+ myssh_do_it, /* do_it */
+ scp_done, /* done */
+ ZERO_NULL, /* do_more */
+ myssh_connect, /* connect_it */
+ myssh_multi_statemach, /* connecting */
+ scp_doing, /* doing */
+ myssh_pollset, /* proto_pollset */
+ myssh_pollset, /* doing_pollset */
+ ZERO_NULL, /* domore_pollset */
+ myssh_pollset, /* perform_pollset */
+ scp_disconnect, /* disconnect */
+ ZERO_NULL, /* write_resp */
+ 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 */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
+ PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE
+};
+
+/*
+ * SFTP protocol handler.
+ */
+const struct Curl_handler Curl_handler_sftp = {
+ "SFTP", /* scheme */
+ myssh_setup_connection, /* setup_connection */
+ myssh_do_it, /* do_it */
+ sftp_done, /* done */
+ ZERO_NULL, /* do_more */
+ myssh_connect, /* connect_it */
+ myssh_multi_statemach, /* connecting */
+ sftp_doing, /* doing */
+ myssh_pollset, /* proto_pollset */
+ myssh_pollset, /* doing_pollset */
+ ZERO_NULL, /* domore_pollset */
+ myssh_pollset, /* perform_pollset */
+ sftp_disconnect, /* disconnect */
+ ZERO_NULL, /* write_resp */
+ 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 */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
+ PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE
+};
+
#endif /* USE_LIBSSH */
#include "../curlx/strparse.h"
#include "../curlx/base64.h" /* for base64 encoding/decoding */
-/* 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,
- 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);
-static CURLcode scp_done(struct Curl_easy *data, CURLcode c, bool premature);
-static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done);
-static CURLcode scp_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool dead_connection);
-static CURLcode sftp_done(struct Curl_easy *data, CURLcode, bool premature);
-static CURLcode sftp_doing(struct Curl_easy *data, bool *dophase_done);
-static CURLcode sftp_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool dead);
-static CURLcode sftp_perform(struct Curl_easy *data, bool *connected,
- bool *dophase_done);
-static CURLcode ssh_pollset(struct Curl_easy *data,
- struct easy_pollset *ps);
-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.
- */
+static const char *sftp_libssh2_strerror(unsigned long err)
+{
+ switch(err) {
+ case LIBSSH2_FX_NO_SUCH_FILE:
+ return "No such file or directory";
-const struct Curl_handler Curl_handler_scp = {
- "SCP", /* scheme */
- ssh_setup_connection, /* setup_connection */
- ssh_do, /* do_it */
- scp_done, /* done */
- ZERO_NULL, /* do_more */
- ssh_connect, /* connect_it */
- ssh_multi_statemach, /* connecting */
- scp_doing, /* doing */
- ssh_pollset, /* proto_pollset */
- ssh_pollset, /* doing_pollset */
- ZERO_NULL, /* domore_pollset */
- ssh_pollset, /* perform_pollset */
- scp_disconnect, /* disconnect */
- ZERO_NULL, /* write_resp */
- ZERO_NULL, /* write_resp_hd */
- ZERO_NULL, /* connection_check */
- ssh_attach, /* attach */
- ZERO_NULL, /* follow */
- PORT_SSH, /* defport */
- CURLPROTO_SCP, /* protocol */
- CURLPROTO_SCP, /* family */
- PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
- PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE
-};
+ case LIBSSH2_FX_PERMISSION_DENIED:
+ return "Permission denied";
-/*
- * SFTP protocol handler.
- */
+ case LIBSSH2_FX_FAILURE:
+ return "Operation failed";
-const struct Curl_handler Curl_handler_sftp = {
- "SFTP", /* scheme */
- ssh_setup_connection, /* setup_connection */
- ssh_do, /* do_it */
- sftp_done, /* done */
- ZERO_NULL, /* do_more */
- ssh_connect, /* connect_it */
- ssh_multi_statemach, /* connecting */
- sftp_doing, /* doing */
- ssh_pollset, /* proto_pollset */
- ssh_pollset, /* doing_pollset */
- ZERO_NULL, /* domore_pollset */
- ssh_pollset, /* perform_pollset */
- sftp_disconnect, /* disconnect */
- ZERO_NULL, /* write_resp */
- ZERO_NULL, /* write_resp_hd */
- ZERO_NULL, /* connection_check */
- ssh_attach, /* attach */
- ZERO_NULL, /* follow */
- PORT_SSH, /* defport */
- CURLPROTO_SFTP, /* protocol */
- CURLPROTO_SFTP, /* family */
- PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
- PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE
-};
+ case LIBSSH2_FX_BAD_MESSAGE:
+ return "Bad message from SFTP server";
+
+ case LIBSSH2_FX_NO_CONNECTION:
+ return "Not connected to SFTP server";
+
+ case LIBSSH2_FX_CONNECTION_LOST:
+ return "Connection to SFTP server lost";
+
+ case LIBSSH2_FX_OP_UNSUPPORTED:
+ return "Operation not supported by SFTP server";
+
+ case LIBSSH2_FX_INVALID_HANDLE:
+ return "Invalid handle";
+
+ case LIBSSH2_FX_NO_SUCH_PATH:
+ return "No such file or directory";
+
+ case LIBSSH2_FX_FILE_ALREADY_EXISTS:
+ return "File already exists";
+
+ case LIBSSH2_FX_WRITE_PROTECT:
+ return "File is write protected";
+
+ case LIBSSH2_FX_NO_MEDIA:
+ return "No media";
+
+ case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
+ return "Disk full";
+
+ case LIBSSH2_FX_QUOTA_EXCEEDED:
+ return "User quota exceeded";
+
+ case LIBSSH2_FX_UNKNOWN_PRINCIPLE:
+ return "Unknown principle";
+
+ case LIBSSH2_FX_LOCK_CONFlICT:
+ return "File lock conflict";
+
+ case LIBSSH2_FX_DIR_NOT_EMPTY:
+ return "Directory not empty";
+
+ case LIBSSH2_FX_NOT_A_DIRECTORY:
+ return "Not a directory";
+
+ case LIBSSH2_FX_INVALID_FILENAME:
+ return "Invalid filename";
+
+ case LIBSSH2_FX_LINK_LOOP:
+ return "Link points to itself";
+ }
+ return "Unknown error in libssh2";
+}
static void kbd_callback(const char *name, int name_len,
const char *instruction, int instruction_len,
myssh_state(data, sshc, SSH_SESSION_FREE);
return CURLE_OK;
}
+
+static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data,
+ bool block)
+{
+ int rc;
+
+ if(sshc->kh) {
+ libssh2_knownhost_free(sshc->kh);
+ sshc->kh = NULL;
+ }
+
+ if(sshc->ssh_agent) {
+ rc = libssh2_agent_disconnect(sshc->ssh_agent);
+ 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);
+ }
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ 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((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);
+ }
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ sshc->sftp_handle = NULL;
+ }
+
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_free(sshc->ssh_channel);
+ 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);
+ }
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ sshc->ssh_channel = NULL;
+ }
+
+ if(sshc->sftp_session) {
+ rc = libssh2_sftp_shutdown(sshc->sftp_session);
+ if((rc < 0) && data) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
+ infof(data, "Failed to stop libssh2 sftp subsystem: %d %s", rc, err_msg);
+ }
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ sshc->sftp_session = NULL;
+ }
+
+ if(sshc->ssh_session) {
+ rc = libssh2_session_free(sshc->ssh_session);
+ 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);
+ }
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ 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);
+
+ 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
* to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
* meaning it wants to be called again when the socket is ready
*/
-
static CURLcode ssh_statemachine(struct Curl_easy *data,
struct ssh_conn *sshc,
struct SSHPROTO *sshp,
return result;
}
-/*
- * The DO function is generic for both protocols. There was previously two
- * separate ones but this way means less duplicated code.
- */
-
-static CURLcode ssh_do(struct Curl_easy *data, bool *done)
-{
- CURLcode result;
- bool connected = FALSE;
- struct connectdata *conn = data->conn;
- 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->secondCreateDirs = 0; /* reset the create directory attempt state
- variable */
-
- Curl_pgrsReset(data);
-
- if(conn->handler->protocol & CURLPROTO_SCP)
- result = scp_perform(data, &connected, done);
- else
- result = sftp_perform(data, &connected, done);
-
- return result;
-}
-
-static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data,
- bool block)
-{
- int rc;
-
- if(sshc->kh) {
- libssh2_knownhost_free(sshc->kh);
- sshc->kh = NULL;
- }
-
- if(sshc->ssh_agent) {
- rc = libssh2_agent_disconnect(sshc->ssh_agent);
- 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);
- }
- if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
- return CURLE_AGAIN;
-
- 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((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);
- }
- if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
- return CURLE_AGAIN;
-
- sshc->sftp_handle = NULL;
- }
-
- if(sshc->ssh_channel) {
- rc = libssh2_channel_free(sshc->ssh_channel);
- 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);
- }
- if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
- return CURLE_AGAIN;
-
- sshc->ssh_channel = NULL;
- }
-
- if(sshc->sftp_session) {
- rc = libssh2_sftp_shutdown(sshc->sftp_session);
- if((rc < 0) && data) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
- infof(data, "Failed to stop libssh2 sftp subsystem: %d %s", rc, err_msg);
- }
- if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
- return CURLE_AGAIN;
-
- sshc->sftp_session = NULL;
- }
-
- if(sshc->ssh_session) {
- rc = libssh2_session_free(sshc->ssh_session);
- 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);
- }
- if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
- return CURLE_AGAIN;
-
- 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);
-
- 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 */
return CURLE_OK;
}
-static const char *sftp_libssh2_strerror(unsigned long err)
+/*
+ * The DO function is generic for both protocols. There was previously two
+ * separate ones but this way means less duplicated code.
+ */
+static CURLcode ssh_do(struct Curl_easy *data, bool *done)
{
- switch(err) {
- case LIBSSH2_FX_NO_SUCH_FILE:
- return "No such file or directory";
-
- case LIBSSH2_FX_PERMISSION_DENIED:
- return "Permission denied";
-
- case LIBSSH2_FX_FAILURE:
- return "Operation failed";
-
- case LIBSSH2_FX_BAD_MESSAGE:
- return "Bad message from SFTP server";
-
- case LIBSSH2_FX_NO_CONNECTION:
- return "Not connected to SFTP server";
-
- case LIBSSH2_FX_CONNECTION_LOST:
- return "Connection to SFTP server lost";
-
- case LIBSSH2_FX_OP_UNSUPPORTED:
- return "Operation not supported by SFTP server";
-
- case LIBSSH2_FX_INVALID_HANDLE:
- return "Invalid handle";
-
- case LIBSSH2_FX_NO_SUCH_PATH:
- return "No such file or directory";
-
- case LIBSSH2_FX_FILE_ALREADY_EXISTS:
- return "File already exists";
-
- case LIBSSH2_FX_WRITE_PROTECT:
- return "File is write protected";
-
- case LIBSSH2_FX_NO_MEDIA:
- return "No media";
-
- case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
- return "Disk full";
-
- case LIBSSH2_FX_QUOTA_EXCEEDED:
- return "User quota exceeded";
-
- case LIBSSH2_FX_UNKNOWN_PRINCIPLE:
- return "Unknown principle";
+ CURLcode result;
+ bool connected = FALSE;
+ struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
- case LIBSSH2_FX_LOCK_CONFlICT:
- return "File lock conflict";
+ *done = FALSE; /* default to false */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
- case LIBSSH2_FX_DIR_NOT_EMPTY:
- return "Directory not empty";
+ data->req.size = -1; /* make sure this is unknown at this point */
+ sshc->secondCreateDirs = 0; /* reset the create directory attempt state
+ variable */
- case LIBSSH2_FX_NOT_A_DIRECTORY:
- return "Not a directory";
+ Curl_pgrsReset(data);
- case LIBSSH2_FX_INVALID_FILENAME:
- return "Invalid filename";
+ if(conn->handler->protocol & CURLPROTO_SCP)
+ result = scp_perform(data, &connected, done);
+ else
+ result = sftp_perform(data, &connected, done);
- case LIBSSH2_FX_LINK_LOOP:
- return "Link points to itself";
- }
- return "Unknown error in libssh2";
+ return result;
}
CURLcode Curl_ssh_init(void)
}
}
}
+
+/*
+ * SCP protocol handler.
+ */
+const struct Curl_handler Curl_handler_scp = {
+ "SCP", /* scheme */
+ ssh_setup_connection, /* setup_connection */
+ ssh_do, /* do_it */
+ scp_done, /* done */
+ ZERO_NULL, /* do_more */
+ ssh_connect, /* connect_it */
+ ssh_multi_statemach, /* connecting */
+ scp_doing, /* doing */
+ ssh_pollset, /* proto_pollset */
+ ssh_pollset, /* doing_pollset */
+ ZERO_NULL, /* domore_pollset */
+ ssh_pollset, /* perform_pollset */
+ scp_disconnect, /* disconnect */
+ ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
+ ZERO_NULL, /* connection_check */
+ ssh_attach, /* attach */
+ ZERO_NULL, /* follow */
+ PORT_SSH, /* defport */
+ CURLPROTO_SCP, /* protocol */
+ CURLPROTO_SCP, /* family */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
+ PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE
+};
+
+/*
+ * SFTP protocol handler.
+ */
+const struct Curl_handler Curl_handler_sftp = {
+ "SFTP", /* scheme */
+ ssh_setup_connection, /* setup_connection */
+ ssh_do, /* do_it */
+ sftp_done, /* done */
+ ZERO_NULL, /* do_more */
+ ssh_connect, /* connect_it */
+ ssh_multi_statemach, /* connecting */
+ sftp_doing, /* doing */
+ ssh_pollset, /* proto_pollset */
+ ssh_pollset, /* doing_pollset */
+ ZERO_NULL, /* domore_pollset */
+ ssh_pollset, /* perform_pollset */
+ sftp_disconnect, /* disconnect */
+ ZERO_NULL, /* write_resp */
+ ZERO_NULL, /* write_resp_hd */
+ ZERO_NULL, /* connection_check */
+ ssh_attach, /* attach */
+ ZERO_NULL, /* follow */
+ PORT_SSH, /* defport */
+ CURLPROTO_SFTP, /* protocol */
+ CURLPROTO_SFTP, /* family */
+ PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */
+ PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE
+};
+
#endif /* USE_LIBSSH2 */