mirror of
https://github.com/upa/mscp.git
synced 2026-02-04 03:24:58 +08:00
598 lines
18 KiB
Diff
598 lines
18 KiB
Diff
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
|
index a64b7708..c6344a5a 100644
|
|
--- a/CMakeLists.txt
|
|
+++ b/CMakeLists.txt
|
|
@@ -1,4 +1,4 @@
|
|
-cmake_minimum_required(VERSION 3.3.0)
|
|
+cmake_minimum_required(VERSION 3.13.0)
|
|
cmake_policy(SET CMP0048 NEW)
|
|
|
|
# Specify search path for CMake modules to be loaded by include()
|
|
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
|
|
index 9de10225..0f3d20ed 100644
|
|
--- a/ConfigureChecks.cmake
|
|
+++ b/ConfigureChecks.cmake
|
|
@@ -258,6 +258,7 @@ if (UNIX)
|
|
check_library_exists(util forkpty "" HAVE_LIBUTIL)
|
|
check_function_exists(cfmakeraw HAVE_CFMAKERAW)
|
|
check_function_exists(__strtoull HAVE___STRTOULL)
|
|
+ check_symbol_exists(TCP_CONGESTION "netinet/tcp.h" HAVE_TCP_CONGESTION)
|
|
endif (UNIX)
|
|
|
|
set(LIBSSH_REQUIRED_LIBRARIES ${_REQUIRED_LIBRARIES} CACHE INTERNAL "libssh required system libraries")
|
|
diff --git a/config.h.cmake b/config.h.cmake
|
|
index cc83734d..f74cd03b 100644
|
|
--- a/config.h.cmake
|
|
+++ b/config.h.cmake
|
|
@@ -237,6 +237,8 @@
|
|
|
|
#cmakedefine HAVE_GCC_BOUNDED_ATTRIBUTE 1
|
|
|
|
+#cmakedefine HAVE_TCP_CONGESTION 1
|
|
+
|
|
/* Define to 1 if you want to enable GSSAPI */
|
|
#cmakedefine WITH_GSSAPI 1
|
|
|
|
diff --git a/include/libssh/buffer.h b/include/libssh/buffer.h
|
|
index 1fce7b76..b64d1455 100644
|
|
--- a/include/libssh/buffer.h
|
|
+++ b/include/libssh/buffer.h
|
|
@@ -37,6 +37,8 @@ int ssh_buffer_add_u8(ssh_buffer buffer, uint8_t data);
|
|
int ssh_buffer_add_u16(ssh_buffer buffer, uint16_t data);
|
|
int ssh_buffer_add_u32(ssh_buffer buffer, uint32_t data);
|
|
int ssh_buffer_add_u64(ssh_buffer buffer, uint64_t data);
|
|
+ssize_t ssh_buffer_add_func(ssh_buffer buffer, ssh_add_func f, size_t max_bytes,
|
|
+ void *userdata);
|
|
|
|
int ssh_buffer_validate_length(struct ssh_buffer_struct *buffer, size_t len);
|
|
|
|
diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h
|
|
index 669a0a96..26b20f3f 100644
|
|
--- a/include/libssh/libssh.h
|
|
+++ b/include/libssh/libssh.h
|
|
@@ -368,6 +368,7 @@ enum ssh_options_e {
|
|
SSH_OPTIONS_HOST,
|
|
SSH_OPTIONS_PORT,
|
|
SSH_OPTIONS_PORT_STR,
|
|
+ SSH_OPTIONS_AI_FAMILY,
|
|
SSH_OPTIONS_FD,
|
|
SSH_OPTIONS_USER,
|
|
SSH_OPTIONS_SSH_DIR,
|
|
@@ -402,6 +403,7 @@ enum ssh_options_e {
|
|
SSH_OPTIONS_GSSAPI_AUTH,
|
|
SSH_OPTIONS_GLOBAL_KNOWNHOSTS,
|
|
SSH_OPTIONS_NODELAY,
|
|
+ SSH_OPTIONS_CCALGO,
|
|
SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES,
|
|
SSH_OPTIONS_PROCESS_CONFIG,
|
|
SSH_OPTIONS_REKEY_DATA,
|
|
@@ -833,6 +835,7 @@ LIBSSH_API const char* ssh_get_hmac_in(ssh_session session);
|
|
LIBSSH_API const char* ssh_get_hmac_out(ssh_session session);
|
|
|
|
LIBSSH_API ssh_buffer ssh_buffer_new(void);
|
|
+LIBSSH_API ssh_buffer ssh_buffer_new_size(uint32_t size, uint32_t headroom);
|
|
LIBSSH_API void ssh_buffer_free(ssh_buffer buffer);
|
|
#define SSH_BUFFER_FREE(x) \
|
|
do { if ((x) != NULL) { ssh_buffer_free(x); x = NULL; } } while(0)
|
|
@@ -843,6 +846,11 @@ LIBSSH_API void *ssh_buffer_get(ssh_buffer buffer);
|
|
LIBSSH_API uint32_t ssh_buffer_get_len(ssh_buffer buffer);
|
|
LIBSSH_API int ssh_session_set_disconnect_message(ssh_session session, const char *message);
|
|
|
|
+typedef ssize_t (*ssh_add_func) (void *ptr, size_t max_bytes, void *userdata);
|
|
+
|
|
+LIBSSH_API const char **ssh_ciphers(void);
|
|
+LIBSSH_API const char **ssh_hmacs(void);
|
|
+
|
|
#ifndef LIBSSH_LEGACY_0_4
|
|
#include "libssh/legacy.h"
|
|
#endif
|
|
diff --git a/include/libssh/session.h b/include/libssh/session.h
|
|
index 97936195..e4fc4fce 100644
|
|
--- a/include/libssh/session.h
|
|
+++ b/include/libssh/session.h
|
|
@@ -249,6 +249,7 @@ struct ssh_session_struct {
|
|
unsigned long timeout; /* seconds */
|
|
unsigned long timeout_usec;
|
|
uint16_t port;
|
|
+ int ai_family;
|
|
socket_t fd;
|
|
int StrictHostKeyChecking;
|
|
char compressionlevel;
|
|
@@ -258,6 +259,7 @@ struct ssh_session_struct {
|
|
int flags;
|
|
int exp_flags;
|
|
int nodelay;
|
|
+ char *ccalgo;
|
|
bool config_processed;
|
|
uint8_t options_seen[SOC_MAX];
|
|
uint64_t rekey_data;
|
|
diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h
|
|
index c713466e..e27fe326 100644
|
|
--- a/include/libssh/sftp.h
|
|
+++ b/include/libssh/sftp.h
|
|
@@ -565,6 +565,10 @@ LIBSSH_API int sftp_async_read(sftp_file file, void *data, uint32_t len, uint32_
|
|
*/
|
|
LIBSSH_API ssize_t sftp_write(sftp_file file, const void *buf, size_t count);
|
|
|
|
+LIBSSH_API ssize_t sftp_async_write(sftp_file file, ssh_add_func f, size_t count,
|
|
+ void *userdata, uint32_t* id);
|
|
+LIBSSH_API int sftp_async_write_end(sftp_file file, uint32_t id, int blocking);
|
|
+
|
|
/**
|
|
* @brief Seek to a specific location in a file.
|
|
*
|
|
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
|
|
index 807313b5..86487087 100644
|
|
--- a/src/CMakeLists.txt
|
|
+++ b/src/CMakeLists.txt
|
|
@@ -448,6 +448,11 @@ if (BUILD_STATIC_LIB)
|
|
if (WIN32)
|
|
target_compile_definitions(ssh-static PUBLIC "LIBSSH_STATIC")
|
|
endif (WIN32)
|
|
+
|
|
+ install(TARGETS ssh-static
|
|
+ EXPORT libssh-config
|
|
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
+ COMPONENT libraries)
|
|
endif (BUILD_STATIC_LIB)
|
|
|
|
message(STATUS "Threads_FOUND=${Threads_FOUND}")
|
|
diff --git a/src/buffer.c b/src/buffer.c
|
|
index 8991e006..e0414801 100644
|
|
--- a/src/buffer.c
|
|
+++ b/src/buffer.c
|
|
@@ -142,6 +142,40 @@ struct ssh_buffer_struct *ssh_buffer_new(void)
|
|
return buf;
|
|
}
|
|
|
|
+/**
|
|
+ * @brief Create a new SSH buffer with a specified size and headroom.
|
|
+ *
|
|
+ * @param[in] len length for newly initialized SSH buffer.
|
|
+ * @param[in] headroom length for headroom
|
|
+ * @return A newly initialized SSH buffer, NULL on error.
|
|
+ */
|
|
+struct ssh_buffer_struct *ssh_buffer_new_size(uint32_t len, uint32_t headroom)
|
|
+{
|
|
+ struct ssh_buffer_struct *buf = NULL;
|
|
+ int rc;
|
|
+
|
|
+ if (len < headroom)
|
|
+ return NULL;
|
|
+
|
|
+ buf = calloc(1, sizeof(struct ssh_buffer_struct));
|
|
+ if (buf == NULL) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ rc = ssh_buffer_allocate_size(buf, len);
|
|
+ if (rc != 0) {
|
|
+ SAFE_FREE(buf);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ buf->pos += headroom;
|
|
+ buf->used += headroom;
|
|
+
|
|
+ buffer_verify(buf);
|
|
+
|
|
+ return buf;
|
|
+}
|
|
+
|
|
/**
|
|
* @brief Deallocate a SSH buffer.
|
|
*
|
|
@@ -329,6 +363,49 @@ int ssh_buffer_add_data(struct ssh_buffer_struct *buffer, const void *data, uint
|
|
return 0;
|
|
}
|
|
|
|
+/**
|
|
+ * @brief Add data at the tail of a buffer by an external function
|
|
+ *
|
|
+ * @param[in] buffer The buffer to add data.
|
|
+ *
|
|
+ * @param[in] f function that adds data to the buffer.
|
|
+ *
|
|
+ * @param[in] max_bytes The maximum length of the data to add.
|
|
+ *
|
|
+ * @return actual bytes added on success, < 0 on error.
|
|
+ */
|
|
+ssize_t ssh_buffer_add_func(struct ssh_buffer_struct *buffer, ssh_add_func f,
|
|
+ size_t max_bytes, void *userdata)
|
|
+{
|
|
+ ssize_t actual;
|
|
+
|
|
+ if (buffer == NULL) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ buffer_verify(buffer);
|
|
+
|
|
+ if (buffer->used + max_bytes < max_bytes) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (buffer->allocated < (buffer->used + max_bytes)) {
|
|
+ if (buffer->pos > 0) {
|
|
+ buffer_shift(buffer);
|
|
+ }
|
|
+ if (realloc_buffer(buffer, buffer->used + max_bytes) < 0) {
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((actual = f(buffer->data + buffer->used, max_bytes, userdata)) < 0)
|
|
+ return -1;
|
|
+
|
|
+ buffer->used += actual;
|
|
+ buffer_verify(buffer);
|
|
+ return actual;
|
|
+}
|
|
+
|
|
/**
|
|
* @brief Ensure the buffer has at least a certain preallocated size.
|
|
*
|
|
diff --git a/src/connect.c b/src/connect.c
|
|
index 15cae644..02ef43b4 100644
|
|
--- a/src/connect.c
|
|
+++ b/src/connect.c
|
|
@@ -114,7 +114,7 @@ static int ssh_connect_socket_close(socket_t s)
|
|
#endif
|
|
}
|
|
|
|
-static int getai(const char *host, int port, struct addrinfo **ai)
|
|
+static int getai(const char *host, int port, int ai_family, struct addrinfo **ai)
|
|
{
|
|
const char *service = NULL;
|
|
struct addrinfo hints;
|
|
@@ -123,7 +123,7 @@ static int getai(const char *host, int port, struct addrinfo **ai)
|
|
ZERO_STRUCT(hints);
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
- hints.ai_family = PF_UNSPEC;
|
|
+ hints.ai_family = ai_family > 0 ? ai_family : PF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
if (port == 0) {
|
|
@@ -156,6 +156,20 @@ static int set_tcp_nodelay(socket_t socket)
|
|
sizeof(opt));
|
|
}
|
|
|
|
+static int set_tcp_ccalgo(socket_t socket, const char *ccalgo)
|
|
+{
|
|
+#ifdef HAVE_TCP_CONGESTION
|
|
+ return setsockopt(socket,
|
|
+ IPPROTO_TCP,
|
|
+ TCP_CONGESTION,
|
|
+ (void *)ccalgo,
|
|
+ strlen(ccalgo));
|
|
+#else
|
|
+ errno = ENOTSUP;
|
|
+ return -1;
|
|
+#endif
|
|
+}
|
|
+
|
|
/**
|
|
* @internal
|
|
*
|
|
@@ -173,7 +187,7 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
|
struct addrinfo *ai = NULL;
|
|
struct addrinfo *itr = NULL;
|
|
|
|
- rc = getai(host, port, &ai);
|
|
+ rc = getai(host, port, session->opts.ai_family, &ai);
|
|
if (rc != 0) {
|
|
ssh_set_error(session, SSH_FATAL,
|
|
"Failed to resolve hostname %s (%s)",
|
|
@@ -199,7 +213,7 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
|
|
|
SSH_LOG(SSH_LOG_PACKET, "Resolving %s", bind_addr);
|
|
|
|
- rc = getai(bind_addr, 0, &bind_ai);
|
|
+ rc = getai(bind_addr, 0, session->opts.ai_family, &bind_ai);
|
|
if (rc != 0) {
|
|
ssh_set_error(session, SSH_FATAL,
|
|
"Failed to resolve bind address %s (%s)",
|
|
@@ -256,6 +270,18 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
|
|
}
|
|
}
|
|
|
|
+ if (session->opts.ccalgo) {
|
|
+ rc = set_tcp_ccalgo(s, session->opts.ccalgo);
|
|
+ if (rc < 0) {
|
|
+ ssh_set_error(session, SSH_FATAL,
|
|
+ "Failed to set TCP_CONGESTION on socket: %s",
|
|
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
|
+ ssh_connect_socket_close(s);
|
|
+ s = -1;
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
errno = 0;
|
|
rc = connect(s, itr->ai_addr, itr->ai_addrlen);
|
|
if (rc == -1 && (errno != 0) && (errno != EINPROGRESS)) {
|
|
diff --git a/src/misc.c b/src/misc.c
|
|
index 7081f12a..e3879fe4 100644
|
|
--- a/src/misc.c
|
|
+++ b/src/misc.c
|
|
@@ -71,6 +71,8 @@
|
|
#include "libssh/priv.h"
|
|
#include "libssh/misc.h"
|
|
#include "libssh/session.h"
|
|
+#include "libssh/wrapper.h"
|
|
+#include "libssh/crypto.h"
|
|
|
|
#ifdef HAVE_LIBGCRYPT
|
|
#define GCRYPT_STRING "/gnutls"
|
|
@@ -2074,4 +2076,40 @@ int ssh_check_hostname_syntax(const char *hostname)
|
|
return SSH_OK;
|
|
}
|
|
|
|
+/**
|
|
+ * @brief Return supported cipher names
|
|
+ * @return The list of cipher names.
|
|
+ */
|
|
+const char **ssh_ciphers(void)
|
|
+{
|
|
+ struct ssh_cipher_struct *tab=ssh_get_ciphertab();
|
|
+ static const char *ciphers[32];
|
|
+ int n;
|
|
+
|
|
+ memset(ciphers, 0, sizeof(*ciphers));
|
|
+
|
|
+ for (n = 0; tab[n].name != NULL; n++) {
|
|
+ ciphers[n] = tab[n].name;
|
|
+ }
|
|
+ return ciphers;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * @brief Return supported hmac names
|
|
+ * @return The list of hmac names.
|
|
+ */
|
|
+const char **ssh_hmacs(void)
|
|
+{
|
|
+ struct ssh_hmac_struct *tab=ssh_get_hmactab();
|
|
+ static const char *hmacs[32];
|
|
+ int n;
|
|
+
|
|
+ memset(hmacs, 0, sizeof(*hmacs));
|
|
+
|
|
+ for (n = 0; tab[n].name != NULL; n++) {
|
|
+ hmacs[n] = tab[n].name;
|
|
+ }
|
|
+ return hmacs;
|
|
+}
|
|
+
|
|
/** @} */
|
|
diff --git a/src/options.c b/src/options.c
|
|
index b3ecffe1..8de24ed6 100644
|
|
--- a/src/options.c
|
|
+++ b/src/options.c
|
|
@@ -217,6 +217,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
|
|
new->opts.gss_delegate_creds = src->opts.gss_delegate_creds;
|
|
new->opts.flags = src->opts.flags;
|
|
new->opts.nodelay = src->opts.nodelay;
|
|
+ new->opts.ccalgo = src->opts.ccalgo;
|
|
new->opts.config_processed = src->opts.config_processed;
|
|
new->common.log_verbosity = src->common.log_verbosity;
|
|
new->common.callbacks = src->common.callbacks;
|
|
@@ -268,6 +269,9 @@ int ssh_options_set_algo(ssh_session session,
|
|
* - SSH_OPTIONS_PORT_STR:
|
|
* The port to connect to (const char *).
|
|
*
|
|
+ * - SSH_OPTIONS_AI_FAMILY:
|
|
+ * The address family for connecting (int *).
|
|
+ *
|
|
* - SSH_OPTIONS_FD:
|
|
* The file descriptor to use (socket_t).\n
|
|
* \n
|
|
@@ -458,6 +462,10 @@ int ssh_options_set_algo(ssh_session session,
|
|
* Set it to disable Nagle's Algorithm (TCP_NODELAY) on the
|
|
* session socket. (int, 0=false)
|
|
*
|
|
+ * - SSH_OPTIONS_CCALGO
|
|
+ * Set it to specify TCP congestion control algorithm on the
|
|
+ * session socket (Linux only). (int, 0=false)
|
|
+ *
|
|
* - SSH_OPTIONS_PROCESS_CONFIG
|
|
* Set it to false to disable automatic processing of per-user
|
|
* and system-wide OpenSSH configuration files. LibSSH
|
|
@@ -571,6 +579,21 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
|
session->opts.port = i & 0xffffU;
|
|
}
|
|
break;
|
|
+ case SSH_OPTIONS_AI_FAMILY:
|
|
+ if (value == NULL) {
|
|
+ session->opts.ai_family = 0;
|
|
+ ssh_set_error_invalid(session);
|
|
+ return -1;
|
|
+ } else {
|
|
+ int *x = (int *) value;
|
|
+ if (*x < 0) {
|
|
+ session->opts.ai_family = 0;
|
|
+ ssh_set_error_invalid(session);
|
|
+ return -1;
|
|
+ }
|
|
+ session->opts.ai_family = *x;
|
|
+ }
|
|
+ break;
|
|
case SSH_OPTIONS_FD:
|
|
if (value == NULL) {
|
|
session->opts.fd = SSH_INVALID_SOCKET;
|
|
@@ -1017,6 +1040,20 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
|
session->opts.nodelay = (*x & 0xff) > 0 ? 1 : 0;
|
|
}
|
|
break;
|
|
+ case SSH_OPTIONS_CCALGO:
|
|
+ v = value;
|
|
+ if (v == NULL || v[0] == '\0') {
|
|
+ ssh_set_error_invalid(session);
|
|
+ return -1;
|
|
+ } else {
|
|
+ SAFE_FREE(session->opts.ccalgo);
|
|
+ session->opts.ccalgo = strdup(v);
|
|
+ if (session->opts.ccalgo == NULL) {
|
|
+ ssh_set_error_oom(session);
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
case SSH_OPTIONS_PROCESS_CONFIG:
|
|
if (value == NULL) {
|
|
ssh_set_error_invalid(session);
|
|
diff --git a/src/session.c b/src/session.c
|
|
index 8c509699..307388e5 100644
|
|
--- a/src/session.c
|
|
+++ b/src/session.c
|
|
@@ -105,9 +105,11 @@ ssh_session ssh_new(void)
|
|
/* OPTIONS */
|
|
session->opts.StrictHostKeyChecking = 1;
|
|
session->opts.port = 22;
|
|
+ session->opts.ai_family = 0;
|
|
session->opts.fd = -1;
|
|
session->opts.compressionlevel = 7;
|
|
session->opts.nodelay = 0;
|
|
+ session->opts.ccalgo = NULL;
|
|
|
|
session->opts.flags = SSH_OPT_FLAG_PASSWORD_AUTH |
|
|
SSH_OPT_FLAG_PUBKEY_AUTH |
|
|
diff --git a/src/sftp.c b/src/sftp.c
|
|
index e01012a8..702623a0 100644
|
|
--- a/src/sftp.c
|
|
+++ b/src/sftp.c
|
|
@@ -2228,6 +2228,132 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
|
|
return -1; /* not reached */
|
|
}
|
|
|
|
+/*
|
|
+ * sftp_async_write is based on and sftp_async_write_end is copied from
|
|
+ * https://github.com/limes-datentechnik-gmbh/libssh
|
|
+ *
|
|
+ * sftp_async_write has some optimizations:
|
|
+ * - use ssh_buffer_new_size() to reduce realoc_buffer.
|
|
+ * - use ssh_buffer_add_func() to avoid memcpy from read buffer to ssh buffer.
|
|
+ */
|
|
+ssize_t sftp_async_write(sftp_file file, ssh_add_func f, size_t count, void *userdata,
|
|
+ uint32_t* id) {
|
|
+ sftp_session sftp = file->sftp;
|
|
+ ssh_buffer buffer;
|
|
+ uint32_t buf_sz;
|
|
+ ssize_t actual;
|
|
+ int len;
|
|
+ int packetlen;
|
|
+ int rc;
|
|
+
|
|
+#define HEADROOM 16
|
|
+ /* sftp_packet_write() prepends a 5-bytes (uint32_t length and
|
|
+ * 1-byte type) header to the head of the payload by
|
|
+ * ssh_buffer_prepend_data(). Inserting headroom by
|
|
+ * ssh_buffer_new_size() eliminates memcpy for prepending the
|
|
+ * header.
|
|
+ */
|
|
+
|
|
+ buf_sz = (HEADROOM + /* for header */
|
|
+ sizeof(uint32_t) + /* id */
|
|
+ ssh_string_len(file->handle) + 4 + /* file->handle */
|
|
+ sizeof(uint64_t) + /* file->offset */
|
|
+ sizeof(uint32_t) + /* count */
|
|
+ count); /* datastring */
|
|
+
|
|
+ buffer = ssh_buffer_new_size(buf_sz, HEADROOM);
|
|
+ if (buffer == NULL) {
|
|
+ ssh_set_error_oom(sftp->session);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ *id = sftp_get_new_id(file->sftp);
|
|
+
|
|
+ rc = ssh_buffer_pack(buffer,
|
|
+ "dSqd",
|
|
+ *id,
|
|
+ file->handle,
|
|
+ file->offset,
|
|
+ count); /* len of datastring */
|
|
+
|
|
+ if (rc != SSH_OK){
|
|
+ ssh_set_error_oom(sftp->session);
|
|
+ ssh_buffer_free(buffer);
|
|
+ return SSH_ERROR;
|
|
+ }
|
|
+
|
|
+ actual = ssh_buffer_add_func(buffer, f, count, userdata);
|
|
+ if (actual < 0){
|
|
+ ssh_set_error_oom(sftp->session);
|
|
+ ssh_buffer_free(buffer);
|
|
+ return SSH_ERROR;
|
|
+ }
|
|
+
|
|
+ packetlen=ssh_buffer_get_len(buffer)+5;
|
|
+ len = sftp_packet_write(file->sftp, SSH_FXP_WRITE, buffer);
|
|
+ ssh_buffer_free(buffer);
|
|
+ if (len < 0) {
|
|
+ return SSH_ERROR;
|
|
+ } else if (len != packetlen) {
|
|
+ ssh_set_error(sftp->session, SSH_FATAL,
|
|
+ "Could only send %d of %d bytes to remote host!", len, packetlen);
|
|
+ SSH_LOG(SSH_LOG_PACKET,
|
|
+ "Could not write as much data as expected");
|
|
+ return SSH_ERROR;
|
|
+ }
|
|
+
|
|
+ file->offset += actual;
|
|
+
|
|
+ return actual;
|
|
+}
|
|
+
|
|
+int sftp_async_write_end(sftp_file file, uint32_t id, int blocking) {
|
|
+ sftp_session sftp = file->sftp;
|
|
+ sftp_message msg = NULL;
|
|
+ sftp_status_message status;
|
|
+
|
|
+ msg = sftp_dequeue(sftp, id);
|
|
+ while (msg == NULL) {
|
|
+ if (!blocking && ssh_channel_poll(sftp->channel, 0) == 0) {
|
|
+ /* we cannot block */
|
|
+ return SSH_AGAIN;
|
|
+ }
|
|
+ if (sftp_read_and_dispatch(sftp) < 0) {
|
|
+ /* something nasty has happened */
|
|
+ return SSH_ERROR;
|
|
+ }
|
|
+ msg = sftp_dequeue(sftp, id);
|
|
+ }
|
|
+
|
|
+ switch (msg->packet_type) {
|
|
+ case SSH_FXP_STATUS:
|
|
+ status = parse_status_msg(msg);
|
|
+ sftp_message_free(msg);
|
|
+ if (status == NULL) {
|
|
+ return SSH_ERROR;
|
|
+ }
|
|
+ sftp_set_error(sftp, status->status);
|
|
+ switch (status->status) {
|
|
+ case SSH_FX_OK:
|
|
+ status_msg_free(status);
|
|
+ return SSH_OK;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
|
|
+ "SFTP server: %s", status->errormsg);
|
|
+ status_msg_free(status);
|
|
+ return SSH_ERROR;
|
|
+ default:
|
|
+ ssh_set_error(sftp->session, SSH_FATAL,
|
|
+ "Received message %d during write!", msg->packet_type);
|
|
+ sftp_message_free(msg);
|
|
+ return SSH_ERROR;
|
|
+ }
|
|
+
|
|
+ return SSH_ERROR; /* not reached */
|
|
+}
|
|
+
|
|
/* Seek to a specific location in a file. */
|
|
int sftp_seek(sftp_file file, uint32_t new_offset) {
|
|
if (file == NULL) {
|