From 907db1f5b9e73a1f088ee4553b729caa2782f36e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net> Date: Wed, 27 Dec 2023 12:39:20 -0500 Subject: [PATCH] More refinement and bug fixes... We can now get the path to the users home directory on the remote system. This introduces the use of error fields in the sftpc_state struct, which should allow result-or-error style stuff pretty easily. --- src/sftp/sftp.h | 17 ++-- src/sftp/sftp_client.c | 183 ++++++++++++++++++++++++++++++++++++----- src/sftp/sftp_pkt.c | 12 ++- src/sftp/sftp_str.c | 2 +- src/syncterm/ssh.c | 12 ++- 5 files changed, 193 insertions(+), 33 deletions(-) diff --git a/src/sftp/sftp.h b/src/sftp/sftp.h index 58a70932b7..5d3e81cd5b 100644 --- a/src/sftp/sftp.h +++ b/src/sftp/sftp.h @@ -5,10 +5,10 @@ extern "C" { #endif -#include <stdbool.h> +#include <eventwrap.h> #include <inttypes.h> - -#include "eventwrap.h" +#include <stdbool.h> +#include <threadwrap.h> // draft-ietf-secsh-filexfer-02 @@ -96,8 +96,14 @@ typedef struct sftp_client_state { xpevent_t recv_event; sftp_rx_pkt_t rxp; sftp_tx_pkt_t txp; - pthread_t thread; + sftp_str_t home; void *cb_data; + sftp_str_t err_msg; + sftp_str_t err_lang; + pthread_t thread; + uint32_t id; + uint32_t err_id; + uint32_t err_code; } *sftpc_state_t; /* sftp_pkt.c */ @@ -110,7 +116,7 @@ bool sftp_have_full_pkt(sftp_rx_pkt_t pkt); void sftp_remove_packet(sftp_rx_pkt_t pkt); uint32_t sftp_get32(sftp_rx_pkt_t pkt); uint32_t sftp_get64(sftp_rx_pkt_t pkt); -sftp_str_t sftp_getstring(sftp_rx_pkt_t pkt, uint8_t **str); +sftp_str_t sftp_getstring(sftp_rx_pkt_t pkt); bool sftp_rx_pkt_append(sftp_rx_pkt_t *pkt, uint8_t *inbuf, uint32_t len); bool sftp_tx_pkt_reset(sftp_tx_pkt_t *pktp); bool sftp_appendbyte(sftp_tx_pkt_t *pktp, uint8_t u8); @@ -132,5 +138,6 @@ void sftpc_finish(sftpc_state_t state); sftpc_state_t sftpc_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data), void *cb_data); bool sftpc_init(sftpc_state_t state); bool sftpc_recv(sftpc_state_t state, uint8_t *buf, uint32_t sz); +bool sftpc_realpath(sftpc_state_t state, char *path, sftp_str_t *ret); #endif diff --git a/src/sftp/sftp_client.c b/src/sftp/sftp_client.c index e2ce1c253f..965954778e 100644 --- a/src/sftp/sftp_client.c +++ b/src/sftp/sftp_client.c @@ -5,6 +5,51 @@ #include "sftp.h" +static uint32_t +get32(sftpc_state_t state) +{ + return sftp_get32(state->rxp); +} + +static uint64_t +get64(sftpc_state_t state) +{ + return sftp_get64(state->rxp); +} + +static sftp_str_t +getstring(sftpc_state_t state) +{ + return sftp_getstring(state->rxp); +} + +static bool +appendbyte(sftpc_state_t state, uint8_t u) +{ + return sftp_appendbyte(&state->txp, u); +} + +static bool +append32(sftpc_state_t state, uint32_t u) +{ + return sftp_append32(&state->txp, u); +} + +static bool +append64(sftpc_state_t state, uint64_t u) +{ + return sftp_append64(&state->txp, u); +} + +static bool +appendstring(sftpc_state_t state, sftp_str_t *s) +{ + bool ret = sftp_appendstring(&state->txp, *s); + free_sftp_str(*s); + *s = NULL; + return ret; +} + void sftpc_finish(sftpc_state_t state) { @@ -41,42 +86,110 @@ sftpc_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data), void *cb_d ret->send_cb = send_cb; ret->cb_data = cb_data; ret->thread = pthread_self(); + ret->id = 0; + ret->err_lang = NULL; + ret->err_msg = NULL; return ret; } -bool -sftpc_init(sftpc_state_t state) +static void +response_handled(sftpc_state_t state) +{ + sftp_remove_packet(state->rxp); + if (!sftp_have_full_pkt(state->rxp)) + ResetEvent(state->recv_event); +} + +static bool +check_state(sftpc_state_t state) { assert(state); if (!state) - goto fail; + return false; assert(state->thread == pthread_self()); if (state->thread != pthread_self()) - goto fail; - if (!sftp_appendbyte(&state->txp, SSH_FXP_INIT)) - goto fail; - if (!sftp_append32(&state->txp, SFTP_VERSION)) - goto fail; + return false; + return true; +} + +static bool +appendheader(sftpc_state_t state, uint8_t type) +{ + if (!check_state(state)) + return false; + state->err_code = 0; + state->err_id = 0; + free_sftp_str(state->err_lang); + state->err_lang = NULL; + free_sftp_str(state->err_msg); + state->err_msg = NULL; + if (!sftp_tx_pkt_reset(&state->txp)) + return false; + if (!sftp_appendbyte(&state->txp, type)) + return false; + if (type != SSH_FXP_INIT) { + if (!sftp_append32(&state->txp, ++state->id)) + return false; + } + return true; +} + +static bool +get_result(sftpc_state_t state) +{ uint8_t *txbuf; size_t txsz; + if (!sftp_prep_tx_packet(state->txp, &txbuf, &txsz)) - goto fail; + return false; if (!state->send_cb(txbuf, txsz, state->cb_data)) - goto fail; - sftp_tx_pkt_reset(&state->txp); + return false; if (WaitForEvent(state->recv_event, INFINITE) != WAIT_OBJECT_0) - goto fail; + return false; + if (state->rxp->type != SSH_FXP_VERSION) { + uint32_t id = sftp_get32(state->rxp); + if (id != state->id) { + response_handled(state); + return false; + } + } + return true; +} + +static void +handle_error(sftpc_state_t state) +{ + if (state->rxp->type == SSH_FXP_STATUS) { + state->err_id = get32(state); + state->err_code = get32(state); + if (state->err_msg != NULL) + free_sftp_str(state->err_msg); + state->err_msg = getstring(state); + if (state->err_lang != NULL) + free_sftp_str(state->err_lang); + state->err_lang = getstring(state); + } + response_handled(state); +} + +bool +sftpc_init(sftpc_state_t state) +{ + if (!appendheader(state, SSH_FXP_INIT)) + return false; + if (!append32(state, SFTP_VERSION)) + return false; + if (!get_result(state)) + return false; if (state->rxp->type != SSH_FXP_VERSION) - goto fail; - if (sftp_get32(state->rxp) != SFTP_VERSION) - goto fail; - sftp_remove_packet(state->rxp); - if (!sftp_have_full_pkt(state->rxp)) - ResetEvent(state->recv_event); + return false; + if (get32(state) != SFTP_VERSION) { + response_handled(state); + return false; + } + response_handled(state); + state->id = 0; return true; -fail: - sftp_tx_pkt_reset(&state->txp); - return false; } bool @@ -88,3 +201,31 @@ sftpc_recv(sftpc_state_t state, uint8_t *buf, uint32_t sz) SetEvent(state->recv_event); return true; } + +bool +sftpc_realpath(sftpc_state_t state, char *path, sftp_str_t *ret) +{ + assert(ret); + if (ret == NULL) + return false; + if (*ret != NULL) + return false; + if (!appendheader(state, SSH_FXP_REALPATH)) + return false; + sftp_str_t pstr = sftp_strdup(path); + if (!appendstring(state, &pstr)) + return false; + if (!get_result(state)) + return false; + if (state->rxp->type == SSH_FXP_NAME) { + if (get32(state) != 1) { + response_handled(state); + return false; + } + *ret = getstring(state); + response_handled(state); + return true; + } + handle_error(state); + return false; +} diff --git a/src/sftp/sftp_pkt.c b/src/sftp/sftp_pkt.c index 064ab91a0f..4c2cc82db7 100644 --- a/src/sftp/sftp_pkt.c +++ b/src/sftp/sftp_pkt.c @@ -121,6 +121,7 @@ sftp_remove_packet(sftp_rx_pkt_t pkt) src += sz; memmove(&pkt->len, src, newsz); pkt->used = newsz; + pkt->cur = 0; // TODO: realloc() smaller? return; } @@ -161,7 +162,7 @@ sftp_get64(sftp_rx_pkt_t pkt) * cursor */ sftp_str_t -sftp_getstring(sftp_rx_pkt_t pkt, uint8_t **str) +sftp_getstring(sftp_rx_pkt_t pkt) { assert(pkt); uint32_t sz = sftp_get32(pkt); @@ -187,11 +188,13 @@ sftp_rx_pkt_append(sftp_rx_pkt_t *pktp, uint8_t *inbuf, uint32_t len) size_t old_sz; size_t new_sz; uint32_t old_used; + uint32_t old_cur; sftp_rx_pkt_t pkt = *pktp; if (pkt == NULL) { old_sz = 0; old_used = 0; + old_cur = 0; new_sz = offsetof(struct sftp_rx_pkt, len) + len; } else { @@ -211,6 +214,7 @@ sftp_rx_pkt_append(sftp_rx_pkt_t *pktp, uint8_t *inbuf, uint32_t len) *pktp = new_buf; pkt = *pktp; pkt->sz = new_sz; + pkt->cur = old_cur; } memcpy(&((uint8_t *)&(pkt->len))[old_used], inbuf, len); pkt->used = old_used + len; @@ -265,6 +269,8 @@ sftp_tx_pkt_reset(sftp_tx_pkt_t *pktp) if (pktp == NULL) return false; sftp_tx_pkt_t pkt = *pktp; + if (pkt == NULL) + return true; pkt->used = 0; if (pkt->sz == SFTP_MIN_PACKET_ALLOC) return true; @@ -310,8 +316,10 @@ sftp_appendstring(sftp_tx_pkt_t *pktp, sftp_str_t s) { assert(pktp); sftp_append32(pktp, s->len); + if (!grow_tx(pktp, s->len)) + return false; sftp_tx_pkt_t pkt = *pktp; - memcpy(&pkt->data[pkt->used], (uint8_t *)s->c_str, s->len); + memcpy(&(&pkt->type)[pkt->used], (uint8_t *)s->c_str, s->len); pkt->used += s->len; return true; } diff --git a/src/sftp/sftp_str.c b/src/sftp/sftp_str.c index 69a641f450..10aa3fa103 100644 --- a/src/sftp/sftp_str.c +++ b/src/sftp/sftp_str.c @@ -2,9 +2,9 @@ #include <stddef.h> #include <stdlib.h> #include <string.h> +#include <xpprintf.h> #include "sftp.h" -#include "xpprintf.h" static sftp_str_t alloc_str(uint32_t len) diff --git a/src/syncterm/ssh.c b/src/syncterm/ssh.c index 3c5df27f57..907bd2020e 100644 --- a/src/syncterm/ssh.c +++ b/src/syncterm/ssh.c @@ -513,10 +513,14 @@ ssh_connect(struct bbslist *bbs) } if (cryptStatusOK(status)) { sftp_state = sftpc_begin(sftp_send, NULL); - if (sftp_state == NULL) - fprintf(stderr, "Failure!\n"); - else if (sftpc_init(sftp_state)) - fprintf(stderr, "Success!\n"); + if (sftp_state != NULL) { + if (sftpc_init(sftp_state)) { + sftp_str_t ret = NULL; + if (sftpc_realpath(sftp_state, ".", &ret)) { + fprintf(stderr, "Home dir: %.*s\n", ret->len, ret->c_str); + } + } + } } } #endif -- GitLab