lib: reorder protocol functions to avoid forward declarations (ssh)

Move protocol handler table to the end of sources, rearrange static
functions in reverse dependency order as necessary.

Closes #20290
This commit is contained in:
Viktor Szakats 2026-01-13 16:30:20 +01:00
parent 7d2c65e6ee
commit f6a83894eb
No known key found for this signature in database
GPG Key ID: B5ABD165E2AEF201
2 changed files with 399 additions and 464 deletions

View File

@ -84,100 +84,6 @@
#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) {
@ -876,6 +782,22 @@ static void myssh_state_init(struct Curl_easy *data,
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)
{
@ -1957,6 +1879,62 @@ static int myssh_in_TRANS_INIT(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
@ -2401,22 +2379,6 @@ static CURLcode myssh_pollset(struct Curl_easy *data,
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)
@ -2692,89 +2654,6 @@ static CURLcode scp_perform(struct Curl_easy *data,
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 */
@ -3136,6 +3015,33 @@ static CURLcode sftp_recv(struct Curl_easy *data, int sockindex,
}
}
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()) {
@ -3155,4 +3061,62 @@ void Curl_ssh_version(char *buffer, size_t buflen)
(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 */

View File

@ -59,92 +59,71 @@
#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,
@ -2573,13 +2552,112 @@ static CURLcode ssh_state_session_disconnect(struct Curl_easy *data,
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,
@ -3494,135 +3572,6 @@ static CURLcode scp_doing(struct Curl_easy *data,
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 */
@ -3897,70 +3846,33 @@ static CURLcode sftp_recv(struct Curl_easy *data, int sockindex,
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";
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_PERMISSION_DENIED:
return "Permission denied";
*done = FALSE; /* default to false */
if(!sshc)
return CURLE_FAILED_INIT;
case LIBSSH2_FX_FAILURE:
return "Operation failed";
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_BAD_MESSAGE:
return "Bad message from SFTP server";
Curl_pgrsReset(data);
case LIBSSH2_FX_NO_CONNECTION:
return "Not connected to SFTP server";
if(conn->handler->protocol & CURLPROTO_SCP)
result = scp_perform(data, &connected, done);
else
result = sftp_perform(data, &connected, done);
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";
return result;
}
CURLcode Curl_ssh_init(void)
@ -3999,4 +3911,63 @@ static void ssh_attach(struct Curl_easy *data, struct connectdata *conn)
}
}
}
/*
* 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 */