Skip to content
Snippets Groups Projects
Commit b7f8abba authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

Start of SFTP support library

parent 9bd53602
No related branches found
No related tags found
No related merge requests found
Pipeline #5105 passed
# uifc/Makefile
########################################
# Makefile for Synchronet SFTP library #
# For use with GNU make for *nix #
########################################
# Macros
#DEBUG = 1 # Comment out for release (non-debug) version
SRC_ROOT = ..
# Cross platform/compiler definitions
include $(SRC_ROOT)/build/Common.gmake # defines clean and output directory rules
CFLAGS += -I$(XPDEV_SRC) $(XPDEV-MT_CFLAGS)
# MT-SFTP Library Link Rule
$(SFTPLIB-MT_BUILD): $(MT_OBJS)
@echo Creating $@ ...
$(QUIET)$(AR) rc $@ $(MT_OBJS)
$(QUIET)$(RANLIB) $@
MT_OBJS = \
$(MTOBJODIR)$(DIRSEP)sftp_pkt$(OFILE) \
$(MTOBJODIR)$(DIRSEP)sftp_str$(OFILE) \
$(MTOBJODIR)$(DIRSEP)sftp_client$(OFILE) \
#ifndef SFTP_SFTP_H
#define SFTP_SFTP_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <inttypes.h>
#include "eventwrap.h"
// draft-ietf-secsh-filexfer-02
#define SSH_FXP_INIT UINT8_C(1)
#define SSH_FXP_VERSION UINT8_C(2)
#define SSH_FXP_OPEN UINT8_C(3)
#define SSH_FXP_CLOSE UINT8_C(4)
#define SSH_FXP_READ UINT8_C(5)
#define SSH_FXP_WRITE UINT8_C(6)
#define SSH_FXP_LSTAT UINT8_C(7)
#define SSH_FXP_FSTAT UINT8_C(8)
#define SSH_FXP_SETSTAT UINT8_C(9)
#define SSH_FXP_FSETSTAT UINT8_C(10)
#define SSH_FXP_OPENDIR UINT8_C(11)
#define SSH_FXP_READDIR UINT8_C(12)
#define SSH_FXP_REMOVE UINT8_C(13)
#define SSH_FXP_MKDIR UINT8_C(14)
#define SSH_FXP_RMDIR UINT8_C(15)
#define SSH_FXP_REALPATH UINT8_C(16)
#define SSH_FXP_STAT UINT8_C(17)
#define SSH_FXP_RENAME UINT8_C(18)
#define SSH_FXP_READLINK UINT8_C(19)
#define SSH_FXP_SYMLINK UINT8_C(20)
#define SSH_FXP_STATUS UINT8_C(101)
#define SSH_FXP_HANDLE UINT8_C(102)
#define SSH_FXP_DATA UINT8_C(103)
#define SSH_FXP_NAME UINT8_C(104)
#define SSH_FXP_ATTRS UINT8_C(105)
#define SSH_FXP_EXTENDED UINT8_C(200)
#define SSH_FXP_EXTENDED_REPLY UINT8_C(201)
#define SSH_FX_OK UINT32_C(0)
#define SSH_FX_EOF UINT32_C(1)
#define SSH_FX_NO_SUCH_FILE UINT32_C(2)
#define SSH_FX_PERMISSION_DENIED UINT32_C(3)
#define SSH_FX_FAILURE UINT32_C(4)
#define SSH_FX_BAD_MESSAGE UINT32_C(5)
#define SSH_FX_NO_CONNECTION UINT32_C(6)
#define SSH_FX_CONNECTION_LOST UINT32_C(7)
#define SSH_FX_OP_UNSUPPORTED UINT32_C(8)
#define SFTP_MIN_PACKET_ALLOC 4096
#define SFTP_VERSION 3
typedef struct sftp_tx_pkt {
uint32_t sz;
uint32_t used;
uint8_t type;
uint8_t data[];
} *sftp_tx_pkt_t;
typedef struct sftp_rx_pkt {
uint32_t cur;
uint32_t sz;
uint32_t used;
uint8_t type;
uint8_t *data;
} *sftp_rx_pkt_t;
typedef struct sftp_string {
uint32_t len;
uint8_t c_str[];
} *sftp_str_t;
struct sftp_extended_file_attribute {
struct sftp_string *type;
struct sftp_string *data;
};
struct sftp_file_attributes {
uint32_t flags;
uint64_t size;
uint32_t uid;
uint32_t gid;
uint32_t perm;
uint32_t atime;
uint32_t mtime;
struct sftp_extended_file_attribute ext[];
};
typedef struct sftp_client_state {
bool (*send_cb)(sftp_tx_pkt_t *pkt, void *cb_data);
xpevent_t recv_event;
sftp_rx_pkt_t rxp;
sftp_tx_pkt_t txp;
pthread_t thread;
void *cb_data;
} *sftpc_state_t;
/* sftp_pkt.c */
const char * const sftp_get_type_name(uint8_t type);
bool sftp_have_pkt_sz(sftp_rx_pkt_t pkt);
bool sftp_have_pkt_type(sftp_rx_pkt_t pkt);
uint32_t sftp_pkt_sz(sftp_rx_pkt_t pkt);
uint8_t sftp_pkt_type(sftp_rx_pkt_t pkt);
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);
bool sftp_rx_pkt_append(sftp_rx_pkt_t *pkt, uint8_t *inbuf, uint32_t len);
bool sftp_appendbyte(sftp_tx_pkt_t *pktp, uint8_t u8);
bool sftp_append32(sftp_tx_pkt_t *pktp, uint32_t u32);
bool sftp_append64(sftp_tx_pkt_t *pktp, uint64_t u);
bool sftp_appendstring(sftp_tx_pkt_t *pktp, sftp_str_t s);
void sftp_free_tx_pkt(sftp_tx_pkt_t pkt);
void sftp_free_rx_pkt(sftp_rx_pkt_t pkt);
/* sftp_str.c */
sftp_str_t sftp_strdup(const char *str);
sftp_str_t sftp_asprintf(const char *format, ...);
sftp_str_t sftp_memdup(uint8_t *buf, uint32_t sz);
void free_sftp_str(sftp_str_t str);
/* sftp_client.c */
void sftpc_finish(sftpc_state_t state);
sftpc_state_t sftpc_begin(bool (*send_cb)(sftp_tx_pkt_t *pkt, 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);
#endif
#include <assert.h>
#include <genwrap.h>
#include <stdlib.h>
#include <threadwrap.h>
#include "sftp.h"
void
sftpc_finish(sftpc_state_t state)
{
assert(state->thread == pthread_self());
// TODO: Close all open handles
while (!CloseEvent(state->recv_event)) {
assert(errno == EBUSY);
if (errno != EBUSY)
break;
SetEvent(state->recv_event);
SLEEP(1);
}
sftp_free_rx_pkt(state->rxp);
sftp_free_tx_pkt(state->txp);
free(state);
}
sftpc_state_t
sftpc_begin(bool (*send_cb)(sftp_tx_pkt_t *pkt, void *cb_data), void *cb_data)
{
sftpc_state_t ret = (sftpc_state_t)malloc(sizeof(sftpc_state_t));
if (ret == NULL)
return NULL;
ret->recv_event = CreateEvent(NULL, TRUE, FALSE, "sftp_recv");
if (ret->recv_event == NULL) {
free(ret);
return NULL;
}
ret->rxp = NULL;
ret->txp = NULL;
ret->send_cb = send_cb;
ret->cb_data = cb_data;
ret->thread = pthread_self();
return ret;
}
bool
sftpc_init(sftpc_state_t state)
{
assert(state);
if (!state)
return false;
assert(state->thread == pthread_self());
if (state->thread != pthread_self())
return false;
if (!sftp_appendbyte(&state->txp, SSH_FXP_INIT))
return false;
if (!sftp_append32(&state->txp, SFTP_VERSION))
return false;
if (WaitForEvent(state->recv_event, INFINITE) != WAIT_OBJECT_0)
return false;
if (state->rxp->type != SSH_FXP_VERSION)
return false;
if (sftp_get32(state->rxp) != SFTP_VERSION)
return false;
sftp_remove_packet(state->rxp);
if (!sftp_have_full_pkt(state->rxp))
ResetEvent(state->recv_event);
return true;
}
bool
sftpc_recv(sftpc_state_t state, uint8_t *buf, uint32_t sz)
{
if (!sftp_rx_pkt_append(&state->rxp, buf, sz))
return false;
if (sftp_have_full_pkt(state->rxp))
SetEvent(state->recv_event);
return true;
}
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <threadwrap.h>
#include <xpendian.h>
#include "sftp.h"
static const struct type_names {
const uint8_t type;
const char * const name;
} type_names[] = {
{SSH_FXP_INIT, "INIT"},
{SSH_FXP_VERSION, "VERSION"},
{SSH_FXP_OPEN, "OPEN"},
{SSH_FXP_CLOSE, "CLOSE"},
{SSH_FXP_READ, "READ"},
{SSH_FXP_WRITE, "WRITE"},
{SSH_FXP_LSTAT, "LSTAT"},
{SSH_FXP_FSTAT, "FSTAT"},
{SSH_FXP_SETSTAT, "SETSTAT"},
{SSH_FXP_FSETSTAT, "FSETSTAT"},
{SSH_FXP_OPENDIR, "OPENDIR"},
{SSH_FXP_READDIR, "READDIR"},
{SSH_FXP_REMOVE, "REMOVE"},
{SSH_FXP_MKDIR, "MKDIR"},
{SSH_FXP_RMDIR, "RMDIR"},
{SSH_FXP_REALPATH, "REALPATH"},
{SSH_FXP_STAT, "STAT"},
{SSH_FXP_RENAME, "RENAME"},
{SSH_FXP_READLINK, "READLINK"},
{SSH_FXP_SYMLINK, "SYMLINK"},
{SSH_FXP_STATUS, "STATUS"},
{SSH_FXP_HANDLE, "HANDLE"},
{SSH_FXP_DATA, "DATA"},
{SSH_FXP_NAME, "NAME"},
{SSH_FXP_ATTRS, "ATTRS"},
{SSH_FXP_EXTENDED, "EXTENDED"},
{SSH_FXP_EXTENDED_REPLY, "EXTENDED REPLY"},
};
static const char * const notfound_type = "<UNKNOWN>";
static int type_cmp(const void *key, const void *name)
{
int k = *(uint8_t *)key;
int n = *(uint8_t *)name;
return k - n;
}
const char * const
sftp_get_type_name(uint8_t type)
{
struct type_names *t = (struct type_names *)(bsearch(&type, type_names, sizeof(type_names) / sizeof(type_names[0]), sizeof(type_names[0]), type_cmp));
if (t == NULL)
return notfound_type;
return t->name;
}
bool
sftp_have_pkt_sz(sftp_rx_pkt_t pkt)
{
if (!pkt)
return false;
return pkt->used >= sizeof(uint32_t);
}
bool
sftp_have_pkt_type(sftp_rx_pkt_t pkt)
{
if (!pkt)
return false;
return pkt->used >= sizeof(uint32_t) + sizeof(uint8_t);
}
uint32_t
sftp_pkt_sz(sftp_rx_pkt_t pkt)
{
if (!pkt)
return false;
assert(sftp_have_pkt_sz(pkt));
return BE_INT32(pkt->used);
}
uint8_t
sftp_pkt_type(sftp_rx_pkt_t pkt)
{
if (!pkt)
return false;
assert(sftp_have_pkt_type(pkt));
return pkt->type;
}
bool
sftp_have_full_pkt(sftp_rx_pkt_t pkt)
{
if (!pkt)
return false;
if (!sftp_have_pkt_sz(pkt))
return false;
if (!sftp_have_pkt_type(pkt))
return false;
uint32_t sz = sftp_pkt_sz(pkt);
if (pkt->used >= sizeof(uint32_t) + sz)
return true;
return false;
}
void
sftp_remove_packet(sftp_rx_pkt_t pkt)
{
if (!pkt)
return;
uint32_t sz = sftp_pkt_sz(pkt);
assert(pkt->sz <= pkt->used);
uint32_t newsz = pkt->used - sz - sizeof(uint32_t);
uint8_t *src = (uint8_t *)&pkt->sz;
src += sizeof(uint32_t);
src += pkt->used;
memmove(&pkt->sz, src, newsz);
pkt->used = newsz;
// TODO: realloc() smaller?
return;
}
#define GET_FUNC_BODY \
assert(pkt); \
if (pkt->cur + sizeof(ret) > pkt->sz) \
return 0; \
memcpy(&ret, &pkt->data[pkt->cur], sizeof(ret)); \
pkt->cur += sizeof(ret)
uint8_t
sftp_getbyte(sftp_rx_pkt_t pkt)
{
uint8_t ret;
GET_FUNC_BODY;
return ret;
}
uint32_t
sftp_get32(sftp_rx_pkt_t pkt)
{
uint32_t ret;
GET_FUNC_BODY;
return BE_INT32(ret);
}
uint32_t
sftp_get64(sftp_rx_pkt_t pkt)
{
uint64_t ret;
GET_FUNC_BODY;
return BE_INT32(ret);
}
/*
* Note that on failure, this returns NULL and does not advance the
* cursor
*/
sftp_str_t
sftp_getstring(sftp_rx_pkt_t pkt, uint8_t **str)
{
assert(pkt);
uint32_t sz = sftp_get32(pkt);
if (pkt->cur + sizeof(sz) > pkt->sz)
return NULL;
sftp_str_t ret = sftp_memdup(&pkt->data[pkt->cur], sz);
if (ret == NULL)
pkt->cur -= sizeof(sz);
else
pkt->cur += sz;
return ret;
}
/*
* Failure is unrecoverable
*/
bool
sftp_rx_pkt_append(sftp_rx_pkt_t *pktp, uint8_t *inbuf, uint32_t len)
{
assert(pktp);
size_t old_sz;
size_t new_sz;
uint32_t old_used;
assert(pktp);
if (!pktp)
return false;
sftp_rx_pkt_t pkt = *pktp;
if (pkt == NULL) {
old_sz = 0;
old_used = 0;
new_sz = offsetof(struct sftp_rx_pkt, used) + len;
}
else {
old_used = pkt->used;
old_sz = pkt->sz;
new_sz = offsetof(struct sftp_rx_pkt, used) + pkt->used + len;
}
if (new_sz > old_sz) {
if (new_sz % SFTP_MIN_PACKET_ALLOC)
new_sz = new_sz / SFTP_MIN_PACKET_ALLOC + SFTP_MIN_PACKET_ALLOC;
void *new_buf = realloc(pkt, new_sz);
if (new_buf == NULL) {
free(pkt);
*pktp = NULL;
return false;
}
*pktp = new_buf;
pkt = *pktp;
}
memcpy(&((uint8_t *)&(pkt->used))[old_used], inbuf, len);
pkt->used += len;
return true;
}
void
sftp_free_rx_pkt(sftp_rx_pkt_t pkt)
{
free(pkt);
}
static bool
grow_tx(sftp_tx_pkt_t *pktp, uint32_t need)
{
assert(pktp);
if (pktp == NULL)
return false;
sftp_tx_pkt_t pkt = *pktp;
assert(pkt->sz >= pkt->used);
size_t newsz;
uint32_t oldsz;
uint32_t oldused;
if (pkt == NULL) {
newsz = offsetof(struct sftp_tx_pkt, type) + need;
oldused = 0;
oldsz = 0;
}
else {
newsz = pkt->used + need;
oldused = pkt->used;
oldsz = pkt->sz;
}
if (newsz > oldsz) {
if (newsz % SFTP_MIN_PACKET_ALLOC)
newsz = newsz / SFTP_MIN_PACKET_ALLOC + SFTP_MIN_PACKET_ALLOC;
void *new_buf = realloc(pkt, newsz);
if (new_buf == NULL)
return false;
*pktp = (sftp_tx_pkt_t)new_buf;
pkt = *pktp;
pkt->sz = newsz;
pkt->used = oldused;
}
return true;
}
#define APPEND_FUNC_BODY(var) \
if (!grow_tx(pktp, sizeof(var))) \
return false; \
sftp_tx_pkt_t pkt = *pktp; \
memcpy(&(&pkt->type)[pkt->used], &var, sizeof(var)); \
pkt->used += sizeof(var); \
return true
bool
sftp_appendbyte(sftp_tx_pkt_t *pktp, uint8_t u8)
{
APPEND_FUNC_BODY(u8);
}
bool
sftp_append32(sftp_tx_pkt_t *pktp, uint32_t u32)
{
uint32_t u = BE_INT32(u32);
APPEND_FUNC_BODY(u);
}
bool
sftp_append64(sftp_tx_pkt_t *pktp, uint64_t u64)
{
uint64_t u = BE_INT64(u64);
APPEND_FUNC_BODY(u);
}
bool
sftp_appendstring(sftp_tx_pkt_t *pktp, sftp_str_t s)
{
assert(pktp);
sftp_append32(pktp, s->len);
sftp_tx_pkt_t pkt = *pktp;
memcpy(&pkt->data[pkt->used], (uint8_t *)s->c_str, s->len);
pkt->used += s->len;
return true;
}
void
sftp_free_tx_pkt(sftp_tx_pkt_t pkt)
{
free(pkt);
}
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "sftp.h"
#include "xpprintf.h"
static sftp_str_t
alloc_str(uint32_t len)
{
sftp_str_t ret = (sftp_str_t)malloc(offsetof(struct sftp_string, c_str) + len + 1);
if (ret != NULL) {
ret->len = len;
ret->c_str[len] = 0;
}
return ret;
}
sftp_str_t
sftp_strdup(const char *str)
{
size_t len = strlen(str);
if (len > UINT32_MAX)
return NULL;
sftp_str_t ret = alloc_str(len);
if (ret == NULL)
return ret;
memcpy(ret->c_str, str, ret->len);
return ret;
}
sftp_str_t
sftp_asprintf(const char *format, ...)
{
va_list va;
va_start(va, format);
char *str = xp_asprintf(format, va);
va_end(va);
if (str == NULL)
return NULL;
size_t len = strlen(str);
if (len > UINT32_MAX) {
free(str);
return NULL;
}
sftp_str_t ret = alloc_str(len);
if (ret == NULL) {
free(str);
return NULL;
}
memcpy(ret->c_str, str, ret->len);
free(str);
return ret;
}
sftp_str_t
sftp_memdup(uint8_t *buf, uint32_t sz)
{
sftp_str_t ret = alloc_str(sz);
if (ret == NULL)
return ret;
memcpy(ret->c_str, buf, sz);
return ret;
}
void
free_sftp_str(sftp_str_t str)
{
free(str);
}
SFTPLIB-MT_BUILD = $(LIBODIR)$(DIRSEP)$(LIBPREFIX)sftp_mt$(LIBFILE)
all: mtlib
mtlib: $(MTOBJODIR) $(LIBODIR) $(XPDEV-MT_LIB) $(SFTPLIB-MT_BUILD)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment