diff options
author | Ryo Nakamura <upa@haeena.net> | 2024-01-06 15:11:20 +0900 |
---|---|---|
committer | Ryo Nakamura <upa@haeena.net> | 2024-01-06 15:11:20 +0900 |
commit | 6f4038a48053da7074ad6bea429289329c0a9e3a (patch) | |
tree | 8d182ee759f09a63ab649178709af3b5694e948a | |
parent | 71a0998e9be628d808b2fe4d6aad26d69ba55b44 (diff) |
bump libssh to libssh-0.10.6-2-g6f1b1e76
libssh 0.10.6 has a regression in IPv6 parsing, so we pick
stable-0.10 that includes the fixes.
https://gitlab.com/libssh/libssh-mirror/-/issues/227
-rw-r--r-- | .github/workflows/build-macos.yml | 2 | ||||
-rw-r--r-- | .github/workflows/build-ubuntu.yml | 2 | ||||
-rw-r--r-- | .github/workflows/codeql.yml | 2 | ||||
-rw-r--r-- | .github/workflows/release.yml | 4 | ||||
-rw-r--r-- | .github/workflows/test.yml | 2 | ||||
-rw-r--r-- | README.md | 2 | ||||
m--------- | libssh | 0 | ||||
-rw-r--r-- | patch/libssh-0.10.6-2-g6f1b1e76.patch | 442 |
8 files changed, 449 insertions, 7 deletions
diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 2eca114..1635808 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -30,7 +30,7 @@ jobs: run: echo "HOMEBREW_PREFIX=$(brew --prefix)" >> $GITHUB_OUTPUT - name: patch to libssh - run: patch -d libssh -p1 < patch/libssh-0.10.6.patch + run: patch -d libssh -p1 < patch/libssh-0.10.6-2-g6f1b1e76.patch - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 5ebd570..efc0c66 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -28,7 +28,7 @@ jobs: sudo ./scripts/install-build-deps.sh - name: patch to libssh - run: patch -d libssh -p1 < patch/libssh-0.10.6.patch + run: patch -d libssh -p1 < patch/libssh-0.10.6-2-g6f1b1e76.patch - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 5bc8ea8..e08d798 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -48,7 +48,7 @@ jobs: sudo ./scripts/install-build-deps.sh - name: patch to libssh - run: patch -d libssh -p1 < patch/libssh-0.10.6.patch + run: patch -d libssh -p1 < patch/libssh-0.10.6-2-g6f1b1e76.patch # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d70157e..83af6fe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: submodules: true - name: patch to libssh - run: patch -d libssh -p1 < patch/libssh-0.10.6.patch + run: patch -d libssh -p1 < patch/libssh-0.10.6-2-g6f1b1e76.patch # TODO: just building docker does not require packages. fix CMakeLists - name: install build dependency @@ -58,7 +58,7 @@ jobs: submodules: true - name: patch to libssh - run: patch -d libssh -p1 < patch/libssh-0.10.6.patch + run: patch -d libssh -p1 < patch/libssh-0.10.6-2-g6f1b1e76.patch - name: Set variables run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0c6c8b1..bb6a0b3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: submodules: true - name: patch to libssh - run: patch -d libssh -p1 < patch/libssh-0.10.6.patch + run: patch -d libssh -p1 < patch/libssh-0.10.6-2-g6f1b1e76.patch # TODO: just building docker does not require libssh. fix CMakeLists - name: install build dependency @@ -76,7 +76,7 @@ cd mscp # prepare patched libssh git submodule update --init -patch -d libssh -p1 < patch/libssh-0.10.6.patch +patch -d libssh -p1 < patch/$(git --git-dir=./libssh/.git describe).patch # install build dependency bash ./scripts/install-build-deps.sh diff --git a/libssh b/libssh -Subproject 10e09e273f69e149389b3e0e5d44b8c221c2e7f +Subproject 6f1b1e76bb38bc89819132e1810e4301ec9034a diff --git a/patch/libssh-0.10.6-2-g6f1b1e76.patch b/patch/libssh-0.10.6-2-g6f1b1e76.patch new file mode 100644 index 0000000..2786da8 --- /dev/null +++ b/patch/libssh-0.10.6-2-g6f1b1e76.patch @@ -0,0 +1,442 @@ +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..b6a93ac7 100644 +--- a/include/libssh/libssh.h ++++ b/include/libssh/libssh.h +@@ -402,6 +402,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 +834,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 +845,8 @@ 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); ++ + #ifndef LIBSSH_LEGACY_0_4 + #include "libssh/legacy.h" + #endif +diff --git a/include/libssh/session.h b/include/libssh/session.h +index 97936195..e4a7f80c 100644 +--- a/include/libssh/session.h ++++ b/include/libssh/session.h +@@ -258,6 +258,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..e7520f40 100644 +--- a/src/connect.c ++++ b/src/connect.c +@@ -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 + * +@@ -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/options.c b/src/options.c +index b3ecffe1..fb966fa1 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; +@@ -458,6 +459,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 +@@ -1017,6 +1022,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..88602b6a 100644 +--- a/src/session.c ++++ b/src/session.c +@@ -108,6 +108,7 @@ ssh_session ssh_new(void) + 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) { |