From: Daniel Stenberg Date: Sun, 11 Jan 2026 10:15:00 +0000 (+0100) Subject: libssh: simplify myssh_statemach_act() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bd3b2a626a33434a1e9e83a1e8573ef2c8c65358;p=thirdparty%2Fcurl.git libssh: simplify myssh_statemach_act() Reduce complexity by splitting out sub functions. Made myssh_quote_error() to reduce code duplication. Closes #20252 --- diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 3e6cb3a832..aa029d3b7a 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -727,6 +727,19 @@ static int myssh_in_SFTP_READDIR_DONE(struct Curl_easy *data, return SSH_NO_ERROR; } +static void myssh_quote_error(struct Curl_easy *data, struct ssh_conn *sshc, + const char *cmd) +{ + if(cmd) + failf(data, "%s command failed: %s", cmd, + ssh_get_error(sshc->ssh_session)); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; +} + static int myssh_in_SFTP_QUOTE_STATVFS(struct Curl_easy *data, struct ssh_conn *sshc) { @@ -734,12 +747,7 @@ static int myssh_in_SFTP_QUOTE_STATVFS(struct Curl_easy *data, statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1); if(!statvfs && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "statvfs command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "statvfs"); return SSH_OK; } else if(statvfs) { @@ -1765,13 +1773,9 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, sftp_attributes_free(sshc->quote_attrs); sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2); if(!sshc->quote_attrs) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Attempt to get SFTP stats failed: %d", sftp_get_error(sshc->sftp_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } @@ -1780,12 +1784,8 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, const char *p = sshc->quote_path1; curl_off_t gid; if(curlx_str_number(&p, &gid, UINT_MAX)) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Syntax error: chgrp gid not a number"); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } sshc->quote_attrs->gid = (uint32_t)gid; @@ -1795,12 +1795,8 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, 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"); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } sshc->quote_attrs->permissions = (mode_t)perms; @@ -1810,12 +1806,8 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, const char *p = sshc->quote_path1; curl_off_t uid; if(curlx_str_number(&p, &uid, UINT_MAX)) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); failf(data, "Syntax error: chown uid not a number"); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } sshc->quote_attrs->uid = (uint32_t)uid; @@ -1836,11 +1828,7 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, } #endif if(fail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } if(date > UINT_MAX) @@ -1871,6 +1859,104 @@ static void conn_forget_socket(struct Curl_easy *data, int sockindex) } } +static void myssh_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 */ + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + 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, + tell the connection to forget about it. This libssh + bug is fixed in 0.10.0. */ + conn_forget_socket(data, FIRSTSOCKET); + } + + SSH_STRING_FREE_CHAR(sshc->homedir); + + myssh_to(data, sshc, SSH_SESSION_FREE); +} + +static int myssh_SSH_SCP_DOWNLOAD(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + curl_off_t bytecount; + int rc = ssh_scp_pull_request(sshc->scp_session); + if(rc != SSH_SCP_REQUEST_NEWFILE) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, CURLE_REMOTE_FILE_NOT_FOUND); + } + + /* download data */ + bytecount = ssh_scp_request_get_size(sshc->scp_session); + data->req.maxdownload = (curl_off_t)bytecount; + Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + data->conn->send_idx = 0; + + myssh_to(data, sshc, SSH_STOP); + return 0; +} + +static int myssh_in_TRANS_INIT(struct Curl_easy *data, struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result; + int rc = 0; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + if(result) { + sshc->actualcode = result; + myssh_to(data, sshc, SSH_STOP); + return 0; + } + + /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ + ssh_set_blocking(sshc->ssh_session, 1); + + if(data->state.upload) { + if(data->state.infilesize < 0) { + failf(data, "SCP requires a known file size for upload"); + return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path); + myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); + } + else { + sshc->scp_session = + ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path); + myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); + } + + if(!sshc->scp_session) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + 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 @@ -1883,9 +1969,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; int rc = SSH_NO_ERROR, err; - const char *err_msg; *block = FALSE; /* we are not blocking by default */ do { @@ -1942,14 +2026,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, break; case SSH_SFTP_QUOTE_INIT: rc = sshp ? myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + CURLE_FAILED_INIT; break; case SSH_SFTP_POSTQUOTE_INIT: rc = myssh_in_SFTP_POSTQUOTE_INIT(data, sshc); break; case SSH_SFTP_QUOTE: - rc = sshp ? myssh_in_SFTP_QUOTE(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = sshp ? myssh_in_SFTP_QUOTE(data, sshc, sshp) : CURLE_FAILED_INIT; break; case SSH_SFTP_NEXT_QUOTE: rc = myssh_in_SFTP_NEXT_QUOTE(data, sshc); @@ -1957,23 +2040,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, case SSH_SFTP_QUOTE_STAT: rc = myssh_in_SFTP_QUOTE_STAT(data, sshc); break; - case SSH_SFTP_QUOTE_SETSTAT: rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, sshc->quote_attrs); if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to set SFTP stats failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - /* sshc->actualcode = sftp_error_to_CURLE(err); - * we do not send the actual error; we return - * the error the libssh2 backend is returning */ + myssh_quote_error(data, sshc, "setstat"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -1985,13 +2058,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "symlink command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "symlink"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2003,12 +2070,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "mkdir command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "mkdir"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2020,13 +2082,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "rename command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "rename"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2037,12 +2093,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "rmdir command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "rmdir"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2053,11 +2104,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc == SSH_AGAIN) break; if(rc && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "rm command failed: %s", ssh_get_error(sshc->ssh_session)); - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + myssh_quote_error(data, sshc, "rm"); break; } myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); @@ -2170,11 +2217,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, case SSH_SFTP_READDIR_INIT: rc = sshp ? myssh_in_SFTP_READDIR_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + CURLE_FAILED_INIT; break; case SSH_SFTP_READDIR: - rc = sshp ? myssh_in_SFTP_READDIR(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = sshp ? myssh_in_SFTP_READDIR(data, sshc, sshp) : CURLE_FAILED_INIT; break; case SSH_SFTP_READDIR_LINK: rc = myssh_in_SFTP_READDIR_LINK(data, sshc); @@ -2187,57 +2233,19 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, break; case SSH_SFTP_DOWNLOAD_INIT: rc = sshp ? myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + CURLE_FAILED_INIT; break; case SSH_SFTP_DOWNLOAD_STAT: rc = myssh_in_SFTP_DOWNLOAD_STAT(data, sshc); break; case SSH_SFTP_CLOSE: - rc = sshp ? myssh_in_SFTP_CLOSE(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = sshp ? myssh_in_SFTP_CLOSE(data, sshc, sshp) : CURLE_FAILED_INIT; break; case SSH_SFTP_SHUTDOWN: rc = myssh_in_SFTP_SHUTDOWN(data, sshc); break; - case SSH_SCP_TRANS_INIT: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); - if(result) { - sshc->actualcode = result; - myssh_to(data, sshc, SSH_STOP); - break; - } - - /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ - ssh_set_blocking(sshc->ssh_session, 1); - - if(data->state.upload) { - if(data->state.infilesize < 0) { - failf(data, "SCP requires a known file size for upload"); - sshc->actualcode = CURLE_UPLOAD_FAILED; - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - break; - } - - sshc->scp_session = - ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path); - myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); - } - else { - sshc->scp_session = - ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path); - myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); - } - - if(!sshc->scp_session) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - } + rc = myssh_in_TRANS_INIT(data, sshc, sshp); break; case SSH_SCP_UPLOAD_INIT: @@ -2247,7 +2255,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, } rc = ssh_scp_init(sshc->scp_session); if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); + const char *err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); break; @@ -2258,7 +2266,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, (int)data->set.new_file_perms); if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); + const char *err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); break; @@ -2275,10 +2283,9 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, break; case SSH_SCP_DOWNLOAD_INIT: - rc = ssh_scp_init(sshc->scp_session); if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); + const char *err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); rc = myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT); break; @@ -2286,28 +2293,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, myssh_to(data, sshc, SSH_SCP_DOWNLOAD); FALLTHROUGH(); - case SSH_SCP_DOWNLOAD: { - curl_off_t bytecount; - - rc = ssh_scp_pull_request(sshc->scp_session); - if(rc != SSH_SCP_REQUEST_NEWFILE) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_REMOTE_FILE_NOT_FOUND); - break; - } - - /* download data */ - bytecount = ssh_scp_request_get_size(sshc->scp_session); - data->req.maxdownload = (curl_off_t)bytecount; - Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); - - /* not set by Curl_xfer_setup to preserve keepon bits */ - conn->send_idx = 0; - - myssh_to(data, sshc, SSH_STOP); + case SSH_SCP_DOWNLOAD: + rc = myssh_SSH_SCP_DOWNLOAD(data, sshc); break; - } + case SSH_SCP_DONE: if(data->state.upload) myssh_to(data, sshc, SSH_SCP_SEND_EOF); @@ -2346,41 +2335,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, FALLTHROUGH(); 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->scp_session) { - ssh_scp_free(sshc->scp_session); - 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, - tell the connection to forget about it. This libssh - bug is fixed in 0.10.0. */ - conn_forget_socket(data, FIRSTSOCKET); - } - - SSH_STRING_FREE_CHAR(sshc->homedir); - - myssh_to(data, sshc, SSH_SESSION_FREE); + myssh_SESSION_DISCONNECT(data, sshc); FALLTHROUGH(); case SSH_SESSION_FREE: 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"); + connclose(data->conn, "SSH session free"); sshc->state = SSH_SESSION_FREE; /* current */ sshc->nextstate = SSH_NO_STATE; myssh_to(data, sshc, SSH_STOP);