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

Start working on the SFTP server code.

The server is a lot thinner, with callbacks for each operation.
parent e53f1731
No related branches found
No related tags found
No related merge requests found
...@@ -2,5 +2,6 @@ MT_OBJS = \ ...@@ -2,5 +2,6 @@ MT_OBJS = \
$(MTOBJODIR)$(DIRSEP)sftp_pkt$(OFILE) \ $(MTOBJODIR)$(DIRSEP)sftp_pkt$(OFILE) \
$(MTOBJODIR)$(DIRSEP)sftp_str$(OFILE) \ $(MTOBJODIR)$(DIRSEP)sftp_str$(OFILE) \
$(MTOBJODIR)$(DIRSEP)sftp_client$(OFILE) \ $(MTOBJODIR)$(DIRSEP)sftp_client$(OFILE) \
$(MTOBJODIR)$(DIRSEP)sftp_server$(OFILE) \
$(MTOBJODIR)$(DIRSEP)sftp_attr$(OFILE) \ $(MTOBJODIR)$(DIRSEP)sftp_attr$(OFILE) \
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
#define SSH_FILEXFER_ATTR_EXTENDED UINT32_C(0x80000000) #define SSH_FILEXFER_ATTR_EXTENDED UINT32_C(0x80000000)
#define SFTP_MIN_PACKET_ALLOC 4096 #define SFTP_MIN_PACKET_ALLOC 4096
#define SFTP_VERSION 3 #define SFTP_VERSION UINT32_C(3)
typedef struct sftp_tx_pkt { typedef struct sftp_tx_pkt {
uint32_t sz; uint32_t sz;
...@@ -90,16 +90,24 @@ struct sftp_extended_file_attribute { ...@@ -90,16 +90,24 @@ struct sftp_extended_file_attribute {
struct sftp_file_attributes; struct sftp_file_attributes;
typedef struct sftp_file_attributes *sftp_file_attr_t; typedef struct sftp_file_attributes *sftp_file_attr_t;
enum sftp_handle_type {
SFTP_HANDLE_TYPE_DIR,
SFTP_HANDLE_TYPE_FILE,
};
typedef sftp_str_t sftp_filehandle_t;
typedef sftp_str_t sftp_dirhandle_t;
typedef struct sftp_client_state { typedef struct sftp_client_state {
bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data); bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data);
xpevent_t recv_event; xpevent_t recv_event;
sftp_rx_pkt_t rxp; sftp_rx_pkt_t rxp;
sftp_tx_pkt_t txp; sftp_tx_pkt_t txp;
sftp_str_t home;
void *cb_data; void *cb_data;
sftp_str_t err_msg; sftp_str_t err_msg;
sftp_str_t err_lang; sftp_str_t err_lang;
pthread_mutex_t mtx; pthread_mutex_t mtx;
uint32_t version;
uint32_t running; uint32_t running;
uint32_t id; uint32_t id;
uint32_t err_id; uint32_t err_id;
...@@ -107,13 +115,37 @@ typedef struct sftp_client_state { ...@@ -107,13 +115,37 @@ typedef struct sftp_client_state {
bool terminating; bool terminating;
} *sftpc_state_t; } *sftpc_state_t;
enum sftp_handle_type { typedef struct sftp_server_state {
SFTP_HANDLE_TYPE_DIR, bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data);
SFTP_HANDLE_TYPE_FILE, sftp_rx_pkt_t rxp;
}; sftp_tx_pkt_t txp;
void *cb_data;
typedef sftp_str_t sftp_filehandle_t; void (*lprintf)(const char *fmt, ...);
typedef sftp_str_t sftp_dirhandle_t; bool (*open)(sftp_str_t filename, uint32_t flags, sftp_file_attr_t attributes, void *cb_data);
bool (*close)(sftp_str_t handle, void *cb_data);
bool (*read)(sftp_filehandle_t handle, uint64_t offset, uint32_t len, void *cb_data);
bool (*write)(sftp_filehandle_t handle, uint64_t offset, sftp_str_t data, void *cb_data);
bool (*remove)(sftp_str_t filename, void *cb_data);
bool (*rename)(sftp_str_t oldpath, sftp_str_t newpath, void *cb_data);
bool (*mkdir)(sftp_str_t path, sftp_file_attr_t attributes, void *cb_data);
bool (*rmdir)(sftp_str_t path, void *cb_data);
bool (*opendir)(sftp_str_t path, void *cb_data);
bool (*readdir)(sftp_str_t handle, void *cb_data);
bool (*stat)(sftp_str_t path, void *cb_data);
bool (*lstat)(sftp_str_t path, void *cb_data);
bool (*fstat)(sftp_filehandle_t handle, void *cb_data);
bool (*setstat)(sftp_str_t path, sftp_file_attr_t attributes, void *cb_data);
bool (*fsetstat)(sftp_filehandle_t handle, sftp_file_attr_t attributes, void *cb_data);
bool (*readlink)(sftp_str_t path, void *cb_data);
bool (*symlink)(sftp_str_t linkpath, sftp_str_t targetpath, void *cb_data);
bool (*realpath)(sftp_str_t path, void *cb_data);
bool (*extended)(sftp_str_t request, sftp_rx_pkt_t pkt, void *cb_data);
pthread_mutex_t mtx;
uint32_t running;
uint32_t id;
uint32_t version;
bool terminating;
} *sftps_state_t;
/* sftp_pkt.c */ /* sftp_pkt.c */
const char * const sftp_get_type_name(uint8_t type); const char * const sftp_get_type_name(uint8_t type);
...@@ -132,6 +164,7 @@ bool sftp_appendbyte(sftp_tx_pkt_t *pktp, uint8_t u8); ...@@ -132,6 +164,7 @@ bool sftp_appendbyte(sftp_tx_pkt_t *pktp, uint8_t u8);
bool sftp_append32(sftp_tx_pkt_t *pktp, uint32_t u32); bool sftp_append32(sftp_tx_pkt_t *pktp, uint32_t u32);
bool sftp_append64(sftp_tx_pkt_t *pktp, uint64_t u); bool sftp_append64(sftp_tx_pkt_t *pktp, uint64_t u);
bool sftp_appendstring(sftp_tx_pkt_t *pktp, sftp_str_t s); bool sftp_appendstring(sftp_tx_pkt_t *pktp, sftp_str_t s);
bool sftp_appendcstring(sftp_tx_pkt_t *pktp, const char *str);
void sftp_free_tx_pkt(sftp_tx_pkt_t pkt); void sftp_free_tx_pkt(sftp_tx_pkt_t pkt);
void sftp_free_rx_pkt(sftp_rx_pkt_t pkt); void sftp_free_rx_pkt(sftp_rx_pkt_t pkt);
bool sftp_prep_tx_packet(sftp_tx_pkt_t pkt, uint8_t **buf, size_t *sz); bool sftp_prep_tx_packet(sftp_tx_pkt_t pkt, uint8_t **buf, size_t *sz);
...@@ -171,5 +204,12 @@ sftp_str_t sftp_fattr_get_ext_type(sftp_file_attr_t fattr, uint32_t index); ...@@ -171,5 +204,12 @@ sftp_str_t sftp_fattr_get_ext_type(sftp_file_attr_t fattr, uint32_t index);
sftp_str_t sftp_fattr_get_ext_data(sftp_file_attr_t fattr, uint32_t index); sftp_str_t sftp_fattr_get_ext_data(sftp_file_attr_t fattr, uint32_t index);
uint32_t sftp_fattr_get_ext_count(sftp_file_attr_t fattr); uint32_t sftp_fattr_get_ext_count(sftp_file_attr_t fattr);
bool sftp_appendfattr(sftp_tx_pkt_t *pktp, sftp_file_attr_t fattr); bool sftp_appendfattr(sftp_tx_pkt_t *pktp, sftp_file_attr_t fattr);
sftp_file_attr_t sftp_getfattr(sftp_rx_pkt_t pkt);
/* sftp_server.c */
bool sftps_recv(sftps_state_t state, uint8_t *buf, uint32_t sz);
sftps_state_t sftps_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data), void *cb_data);
bool sftps_send_packet(sftps_state_t state);
bool sftps_send_error(sftps_state_t state, uint32_t code, const char *msg);
#endif #endif
...@@ -112,6 +112,7 @@ ...@@ -112,6 +112,7 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="sftp_client.c" /> <ClCompile Include="sftp_client.c" />
<ClCompile Include="sftp_server.c" />
<ClCompile Include="sftp_attr.c" /> <ClCompile Include="sftp_attr.c" />
<ClCompile Include="sftp_pkt.c" /> <ClCompile Include="sftp_pkt.c" />
<ClCompile Include="sftp_str.c" /> <ClCompile Include="sftp_str.c" />
......
...@@ -281,3 +281,52 @@ fail: ...@@ -281,3 +281,52 @@ fail:
(*pktp)->used = oldused; (*pktp)->used = oldused;
return false; return false;
} }
sftp_file_attr_t
sftp_getfattr(sftp_rx_pkt_t pkt)
{
sftp_file_attr_t ret = sftp_fattr_alloc();
if (ret == NULL)
return ret;
ret->flags = sftp_get32(pkt);
if (ret->flags & SSH_FILEXFER_ATTR_SIZE) {
ret->size = sftp_get64(pkt);
}
if (ret->flags & SSH_FILEXFER_ATTR_UIDGID) {
ret->uid = sftp_get32(pkt);
ret->gid = sftp_get32(pkt);
}
if (ret->flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
ret->perm = sftp_get32(pkt);
}
if (ret->flags & SSH_FILEXFER_ATTR_ACMODTIME) {
ret->atime = sftp_get32(pkt);
ret->mtime = sftp_get32(pkt);
}
if (ret->flags & SSH_FILEXFER_ATTR_EXTENDED) {
uint32_t extcnt = sftp_get32(pkt);
uint32_t ext;
for (ext = 0; ext < extcnt; ext++) {
sftp_str_t type = sftp_getstring(pkt);
if (type == NULL)
break;
sftp_str_t data = sftp_getstring(pkt);
if (data == NULL) {
free_sftp_str(type);
break;
}
if (!sftp_fattr_add_ext(&ret, type, data)) {
free_sftp_str(type);
free_sftp_str(data);
break;
}
free_sftp_str(type);
free_sftp_str(data);
}
if (ext != extcnt) {
sftp_fattr_free(ret);
return NULL;
}
}
return ret;
}
...@@ -5,35 +5,9 @@ ...@@ -5,35 +5,9 @@
#include "sftp.h" #include "sftp.h"
static uint32_t #define SFTP_STATIC_TYPE sftpc_state_t
get32(sftpc_state_t state) #include "sftp_static.h"
{ #undef SFTP_STATIC_TYPE
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 static bool
append64(sftpc_state_t state, uint64_t u) append64(sftpc_state_t state, uint64_t u)
...@@ -42,9 +16,10 @@ append64(sftpc_state_t state, uint64_t u) ...@@ -42,9 +16,10 @@ append64(sftpc_state_t state, uint64_t u)
} }
static bool static bool
appendfattr(sftpc_state_t state, sftp_file_attr_t fattr) appendstring(sftpc_state_t state, sftp_str_t s)
{ {
return sftp_appendfattr(&state->txp, fattr); bool ret = sftp_appendstring(&state->txp, s);
return ret;
} }
static bool static bool
...@@ -57,85 +32,25 @@ appendandfreestring(sftpc_state_t state, sftp_str_t *s) ...@@ -57,85 +32,25 @@ appendandfreestring(sftpc_state_t state, sftp_str_t *s)
} }
static bool static bool
appendstring(sftpc_state_t state, sftp_str_t s) appendfhandle(sftpc_state_t state, sftp_filehandle_t handle)
{
bool ret = sftp_appendstring(&state->txp, s);
return ret;
}
void
sftpc_finish(sftpc_state_t state)
{
assert(state);
if (state == NULL)
return;
pthread_mutex_lock(&state->mtx);
state->terminating = true;
pthread_mutex_unlock(&state->mtx);
// TODO: Close all open handles
while (!CloseEvent(state->recv_event)) {
assert(errno == EBUSY);
if (errno != EBUSY)
break;
SetEvent(state->recv_event);
SLEEP(1);
}
pthread_mutex_lock(&state->mtx);
while (state->running) {
pthread_mutex_unlock(&state->mtx);
SLEEP(1);
pthread_mutex_lock(&state->mtx);
}
sftp_free_rx_pkt(state->rxp);
sftp_free_tx_pkt(state->txp);
pthread_mutex_unlock(&state->mtx);
pthread_mutex_destroy(&state->mtx);
free(state);
}
sftpc_state_t
sftpc_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data), void *cb_data)
{ {
sftpc_state_t ret = (sftpc_state_t)malloc(sizeof(struct sftp_client_state)); return appendstring(state, (sftp_str_t)handle);
if (ret == NULL)
return NULL;
ret->recv_event = CreateEvent(NULL, TRUE, FALSE, NULL);
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->id = 0;
ret->err_lang = NULL;
ret->err_msg = NULL;
ret->running = 0;
pthread_mutex_init(&ret->mtx, NULL);
ret->terminating = false;
return ret;
} }
static void static bool
response_handled(sftpc_state_t state) appenddhandle(sftpc_state_t state, sftp_dirhandle_t handle)
{ {
sftp_remove_packet(state->rxp); return appendstring(state, (sftp_str_t)handle);
if (!sftp_have_full_pkt(state->rxp))
ResetEvent(state->recv_event);
} }
static bool static bool
check_state(sftpc_state_t state) appendfattr(sftpc_state_t state, sftp_file_attr_t fattr)
{ {
assert(state); return sftp_appendfattr(&state->txp, fattr);
if (!state)
return false;
return true;
} }
static bool static bool
appendheader(sftpc_state_t state, uint8_t type) cappendheader(sftpc_state_t state, uint8_t type)
{ {
state->err_code = 0; state->err_code = 0;
state->err_id = 0; state->err_id = 0;
...@@ -143,27 +58,7 @@ appendheader(sftpc_state_t state, uint8_t type) ...@@ -143,27 +58,7 @@ appendheader(sftpc_state_t state, uint8_t type)
state->err_lang = NULL; state->err_lang = NULL;
free_sftp_str(state->err_msg); free_sftp_str(state->err_msg);
state->err_msg = NULL; state->err_msg = NULL;
if (!sftp_tx_pkt_reset(&state->txp)) return appendheader(state, type);
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
appendfhandle(sftpc_state_t state, sftp_filehandle_t handle)
{
return appendstring(state, (sftp_str_t)handle);
}
static bool
appenddhandle(sftpc_state_t state, sftp_dirhandle_t handle)
{
return appendstring(state, (sftp_str_t)handle);
} }
static bool static bool
...@@ -195,17 +90,27 @@ get_result(sftpc_state_t state) ...@@ -195,17 +90,27 @@ get_result(sftpc_state_t state)
return true; return true;
} }
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 void static void
handle_error(sftpc_state_t state) handle_error(sftpc_state_t state)
{ {
if (state->rxp->type == SSH_FXP_STATUS) { if (state->rxp->type == SSH_FXP_STATUS) {
state->err_code = get32(state); state->err_code = get32(state);
if (state->err_msg != NULL) if (state->version > 2) {
free_sftp_str(state->err_msg); if (state->err_msg != NULL)
state->err_msg = getstring(state); free_sftp_str(state->err_msg);
if (state->err_lang != NULL) state->err_msg = getstring(state);
free_sftp_str(state->err_lang); if (state->err_lang != NULL)
state->err_lang = getstring(state); free_sftp_str(state->err_lang);
state->err_lang = getstring(state);
}
} }
response_handled(state); response_handled(state);
} }
...@@ -228,28 +133,58 @@ parse_handle(sftpc_state_t state, sftp_str_t *handle) ...@@ -228,28 +133,58 @@ parse_handle(sftpc_state_t state, sftp_str_t *handle)
return true; return true;
} }
static bool void
enter_function(sftpc_state_t state) sftpc_finish(sftpc_state_t state)
{ {
if (!check_state(state)) assert(state);
return false; if (state == NULL)
if (pthread_mutex_lock(&state->mtx)) return;
return false; pthread_mutex_lock(&state->mtx);
if (state->terminating) { state->terminating = true;
pthread_mutex_unlock(&state->mtx);
// TODO: Close all open handles
while (!CloseEvent(state->recv_event)) {
assert(errno == EBUSY);
if (errno != EBUSY)
break;
SetEvent(state->recv_event);
SLEEP(1);
}
pthread_mutex_lock(&state->mtx);
while (state->running) {
pthread_mutex_unlock(&state->mtx); pthread_mutex_unlock(&state->mtx);
return false; SLEEP(1);
pthread_mutex_lock(&state->mtx);
} }
state->running++; sftp_free_rx_pkt(state->rxp);
return true; sftp_free_tx_pkt(state->txp);
pthread_mutex_unlock(&state->mtx);
pthread_mutex_destroy(&state->mtx);
free(state);
} }
static bool sftpc_state_t
exit_function(sftpc_state_t state, bool retval) sftpc_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data), void *cb_data)
{ {
assert(state->running > 0); sftpc_state_t ret = (sftpc_state_t)malloc(sizeof(struct sftp_client_state));
state->running--; if (ret == NULL)
pthread_mutex_unlock(&state->mtx); return NULL;
return retval; ret->recv_event = CreateEvent(NULL, TRUE, FALSE, NULL);
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->id = 0;
ret->err_lang = NULL;
ret->err_msg = NULL;
ret->running = 0;
pthread_mutex_init(&ret->mtx, NULL);
ret->terminating = false;
return ret;
} }
bool bool
...@@ -257,7 +192,7 @@ sftpc_init(sftpc_state_t state) ...@@ -257,7 +192,7 @@ sftpc_init(sftpc_state_t state)
{ {
if (!enter_function(state)) if (!enter_function(state))
return false; return false;
if (!appendheader(state, SSH_FXP_INIT)) if (!cappendheader(state, SSH_FXP_INIT))
return exit_function(state, false); return exit_function(state, false);
if (!append32(state, SFTP_VERSION)) if (!append32(state, SFTP_VERSION))
return exit_function(state, false); return exit_function(state, false);
...@@ -265,7 +200,8 @@ sftpc_init(sftpc_state_t state) ...@@ -265,7 +200,8 @@ sftpc_init(sftpc_state_t state)
return exit_function(state, false); return exit_function(state, false);
if (state->rxp->type != SSH_FXP_VERSION) if (state->rxp->type != SSH_FXP_VERSION)
return exit_function(state, false); return exit_function(state, false);
if (get32(state) != SFTP_VERSION) { state->version = get32(state);
if (state->version > SFTP_VERSION) {
response_handled(state); response_handled(state);
return exit_function(state, false); return exit_function(state, false);
} }
...@@ -296,7 +232,7 @@ sftpc_realpath(sftpc_state_t state, char *path, sftp_str_t *ret) ...@@ -296,7 +232,7 @@ sftpc_realpath(sftpc_state_t state, char *path, sftp_str_t *ret)
return exit_function(state, false); return exit_function(state, false);
if (*ret != NULL) if (*ret != NULL)
return exit_function(state, false); return exit_function(state, false);
if (!appendheader(state, SSH_FXP_REALPATH)) if (!cappendheader(state, SSH_FXP_REALPATH))
return exit_function(state, false); return exit_function(state, false);
sftp_str_t pstr = sftp_strdup(path); sftp_str_t pstr = sftp_strdup(path);
if (!appendandfreestring(state, &pstr)) if (!appendandfreestring(state, &pstr))
...@@ -330,7 +266,7 @@ sftpc_open(sftpc_state_t state, char *path, uint32_t flags, sftp_file_attr_t att ...@@ -330,7 +266,7 @@ sftpc_open(sftpc_state_t state, char *path, uint32_t flags, sftp_file_attr_t att
assert(!*handle); assert(!*handle);
if (*handle != NULL) if (*handle != NULL)
return exit_function(state, false); return exit_function(state, false);
if (!appendheader(state, SSH_FXP_OPEN)) if (!cappendheader(state, SSH_FXP_OPEN))
return exit_function(state, false); return exit_function(state, false);
sftp_str_t pstr = sftp_strdup(path); sftp_str_t pstr = sftp_strdup(path);
if (!appendandfreestring(state, &pstr)) if (!appendandfreestring(state, &pstr))
...@@ -369,7 +305,7 @@ sftpc_close(sftpc_state_t state, sftp_filehandle_t *handle) ...@@ -369,7 +305,7 @@ sftpc_close(sftpc_state_t state, sftp_filehandle_t *handle)
{ {
if (!enter_function(state)) if (!enter_function(state))
return false; return false;
if (!appendheader(state, SSH_FXP_CLOSE)) if (!cappendheader(state, SSH_FXP_CLOSE))
return exit_function(state, false); return exit_function(state, false);
if (!appendfhandle(state, *handle)) if (!appendfhandle(state, *handle))
return exit_function(state, false); return exit_function(state, false);
...@@ -392,7 +328,7 @@ sftpc_read(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, uint3 ...@@ -392,7 +328,7 @@ sftpc_read(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, uint3
assert(*ret == NULL); assert(*ret == NULL);
if (*ret != NULL) if (*ret != NULL)
return exit_function(state, false); return exit_function(state, false);
if (!appendheader(state, SSH_FXP_READ)) if (!cappendheader(state, SSH_FXP_READ))
return exit_function(state, false); return exit_function(state, false);
if (!appendfhandle(state, handle)) if (!appendfhandle(state, handle))
return exit_function(state, false); return exit_function(state, false);
...@@ -419,7 +355,7 @@ sftpc_write(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, sftp ...@@ -419,7 +355,7 @@ sftpc_write(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, sftp
assert(data); assert(data);
if (data == NULL) if (data == NULL)
return exit_function(state, false); return exit_function(state, false);
if (!appendheader(state, SSH_FXP_WRITE)) if (!cappendheader(state, SSH_FXP_WRITE))
return exit_function(state, false); return exit_function(state, false);
if (!appendfhandle(state, handle)) if (!appendfhandle(state, handle))
return exit_function(state, false); return exit_function(state, false);
......
...@@ -334,6 +334,40 @@ sftp_appendstring(sftp_tx_pkt_t *pktp, sftp_str_t s) ...@@ -334,6 +334,40 @@ sftp_appendstring(sftp_tx_pkt_t *pktp, sftp_str_t s)
return true; return true;
} }
bool
sftp_appendcstring(sftp_tx_pkt_t *pktp, const char *str)
{
uint32_t oldused;
uint32_t len;
size_t sz;
assert(pktp);
if (*pktp == NULL)
oldused = 0;
else
oldused = (*pktp)->used;
assert(str);
if (str == NULL)
oldused = 0;
else
oldused = (*pktp)->used;
sz = strlen(str);
if (sz > UINT32_MAX)
return false;
len = sz;
if (!sftp_append32(pktp, len))
return false;
if (!grow_tx(pktp, len)) {
if (*pktp != NULL)
(*pktp)->used = oldused;
return false;
}
sftp_tx_pkt_t pkt = *pktp;
memcpy(&(&pkt->type)[pkt->used], str, len);
pkt->used += len;
return true;
}
bool bool
sftp_prep_tx_packet(sftp_tx_pkt_t pkt, uint8_t **buf, size_t *sz) sftp_prep_tx_packet(sftp_tx_pkt_t pkt, uint8_t **buf, size_t *sz)
{ {
......
#include <assert.h>
#include <genwrap.h>
#include <stdlib.h>
#include <threadwrap.h>
#include "sftp.h"
#define SFTP_STATIC_TYPE sftps_state_t
#include "sftp_static.h"
#undef SFTP_STATIC_TYPE
static uint64_t
get64(sftps_state_t state)
{
return sftp_get64(state->rxp);
}
static bool
appendcstring(sftps_state_t state, const char *s)
{
bool ret = sftp_appendcstring(&state->txp, s);
return ret;
}
static bool
init(sftps_state_t state)
{
state->version = get32(state);
if (state->version > SFTP_VERSION)
state->version = SFTP_VERSION;
if (!appendheader(state, state->version))
return false;
if (!append32(state, SFTP_VERSION))
return false;
return sftps_send_packet(state);
}
sftps_state_t
sftps_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data), void *cb_data)
{
sftps_state_t ret = (sftps_state_t)calloc(1, sizeof(struct sftp_server_state));
if (ret == NULL)
return NULL;
ret->rxp = NULL;
ret->txp = NULL;
ret->send_cb = send_cb;
ret->cb_data = cb_data;
ret->id = 0;
ret->running = 0;
pthread_mutex_init(&ret->mtx, NULL);
ret->terminating = false;
return ret;
}
bool
sftps_send_packet(sftps_state_t state)
{
uint8_t *txbuf;
size_t txsz;
if (!sftp_prep_tx_packet(state->txp, &txbuf, &txsz))
return false;
if (!state->send_cb(txbuf, txsz, state->cb_data))
return false;
return true;
}
bool
sftps_send_error(sftps_state_t state, uint32_t code, const char *msg)
{
if (!appendheader(state, SSH_FXP_STATUS))
return false;
if (!append32(state, state->id))
return false;
if (!append32(state, code))
return false;
if (state->version >= 3) {
if (!appendcstring(state, msg))
return false;
if (!appendcstring(state, "en-CA"))
return false;
}
return sftps_send_packet(state);
}
static bool
s_open(sftps_state_t state)
{
bool ret;
sftp_str_t fname;
uint32_t flags;
sftp_file_attr_t attrs;
state->id = get32(state);
fname = getstring(state);
if (fname == NULL)
return false;
flags = get32(state);
attrs = sftp_getfattr(state->rxp);
if (attrs == NULL) {
free_sftp_str(fname);
return false;
}
ret = state->open(fname, flags, attrs, state->cb_data);
sftp_fattr_free(attrs);
free_sftp_str(fname);
return ret;
}
static bool
s_read(sftps_state_t state)
{
bool ret;
sftp_filehandle_t handle;
uint64_t offset;
uint32_t len;
state->id = get32(state);
handle = getstring(state);
if (handle == NULL)
return false;
offset = get64(state);
len = get32(state);
ret = state->read(handle, offset, len, state->cb_data);
free_sftp_str(handle);
return ret;
}
static bool
s_write(sftps_state_t state)
{
bool ret;
sftp_filehandle_t handle;
uint64_t offset;
sftp_str_t data;
state->id = get32(state);
handle = getstring(state);
if (handle == NULL)
return false;
offset = get64(state);
data = getstring(state);
if (data == NULL) {
free_sftp_str(handle);
return false;
}
ret = state->write(handle, offset, data, state->cb_data);
free_sftp_str(handle);
free_sftp_str(data);
return ret;
}
static bool
s_id_str(sftps_state_t state, bool (*cb)(sftp_str_t, void *))
{
bool ret;
sftp_str_t str;
state->id = get32(state);
str = getstring(state);
if (str == NULL)
return false;
ret = cb(str, state->cb_data);
free_sftp_str(str);
return ret;
}
static bool
s_fstat(sftps_state_t state)
{
bool ret;
sftp_filehandle_t handle;
state->id = get32(state);
handle = getstring(state);
if (handle == NULL)
return false;
ret = state->fstat(handle, state->cb_data);
free_sftp_str(handle);
return ret;
}
static bool
s_id_str_attr(sftps_state_t state, bool (*cb)(sftp_str_t, sftp_file_attr_t, void *))
{
bool ret;
sftp_str_t str;
sftp_file_attr_t attrs;
state->id = get32(state);
str = getstring(state);
if (str == NULL)
return false;
attrs = sftp_getfattr(state->rxp);
if (attrs == NULL) {
free_sftp_str(str);
return false;
}
ret = cb(str, attrs, state->cb_data);
free_sftp_str(str);
sftp_fattr_free(attrs);
return ret;
}
bool
s_fsetstat(sftps_state_t state)
{
bool ret;
sftp_filehandle_t handle;
sftp_file_attr_t attrs;
state->id = get32(state);
handle = getstring(state);
if (handle == NULL)
return false;
attrs = sftp_getfattr(state->rxp);
if (attrs == NULL) {
free_sftp_str(handle);
return false;
}
ret = state->fsetstat(handle, attrs, state->cb_data);
free_sftp_str(handle);
sftp_fattr_free(attrs);
return ret;
}
static bool
s_id_str_str(sftps_state_t state, bool (*cb)(sftp_str_t, sftp_str_t, void *))
{
bool ret;
sftp_str_t str1;
sftp_str_t str2;
state->id = get32(state);
str1 = getstring(state);
if (str1 == NULL)
return false;
str2 = getstring(state);
if (str2 == NULL) {
free_sftp_str(str1);
return false;
}
ret = cb(str1, str2, state->cb_data);
free_sftp_str(str1);
free_sftp_str(str2);
return ret;
}
bool
sftps_recv(sftps_state_t state, uint8_t *buf, uint32_t sz)
{
if (!enter_function(state))
return false;
if (!sftp_rx_pkt_append(&state->rxp, buf, sz))
return exit_function(state, false);
while (sftp_have_full_pkt(state->rxp)) {
bool handled = false;
switch(state->rxp->type) {
case SSH_FXP_INIT:
if (!init(state))
return exit_function(state, false);
break;
case SSH_FXP_OPEN:
if (state->open) {
if (!s_open(state))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_CLOSE:
if (state->close) {
if (!s_id_str(state, state->close))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_READ:
if (state->read) {
if (!s_read(state))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_WRITE:
if (state->write) {
if (!s_write(state))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_LSTAT:
if (state->lstat) {
if (!s_id_str(state, state->lstat))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_FSTAT:
if (state->fstat) {
if (!s_fstat(state))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_SETSTAT:
if (state->setstat) {
if (!s_id_str_attr(state, state->setstat))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_FSETSTAT:
if (state->fsetstat) {
if (!s_fsetstat(state))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_OPENDIR:
if (state->opendir) {
if (!s_id_str(state, state->opendir))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_READDIR:
if (state->readdir) {
if (!s_id_str(state, state->readdir))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_REMOVE:
if (state->remove) {
if (!s_id_str(state, state->remove))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_MKDIR:
if (state->mkdir) {
if (!s_id_str_attr(state, state->mkdir))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_RMDIR:
if (state->rmdir) {
if (!s_id_str(state, state->rmdir))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_REALPATH:
if (state->realpath) {
if (!s_id_str(state, state->realpath))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_STAT:
if (state->stat) {
if (!s_id_str(state, state->stat))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_RENAME:
if (state->version >= 2 && state->rename) {
if (!s_id_str_str(state, state->rename))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_READLINK:
if (state->version >= 3 && state->readlink) {
if (!s_id_str(state, state->readlink))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_SYMLINK:
if (state->version >= 3 && state->symlink) {
if (!s_id_str_str(state, state->symlink))
return exit_function(state, false);
handled = true;
}
break;
case SSH_FXP_EXTENDED:
if (state->version >= 3 && state->extended) {
state->id = get32(state);
sftp_str_t request = getstring(state);
if (request == NULL)
return exit_function(state, false);
handled = state->extended(request, state->rxp, state->cb_data);
free_sftp_str(request);
}
default:
break;
}
if (!handled) {
if (state->lprintf)
state->lprintf("Unhandled request type: %s (%d)", sftp_get_type_name(state->rxp->type), state->rxp->type);
state->id = get32(state);
if (!sftps_send_error(state, SSH_FX_OP_UNSUPPORTED, "Operation not implemented"))
return exit_function(state, false);
}
sftp_remove_packet(state->rxp);
}
return exit_function(state, true);
}
// Static functions "shared" between client and server
static uint32_t
get32(SFTP_STATIC_TYPE state)
{
return sftp_get32(state->rxp);
}
static sftp_str_t
getstring(SFTP_STATIC_TYPE state)
{
return sftp_getstring(state->rxp);
}
static bool
append32(SFTP_STATIC_TYPE state, uint32_t u)
{
return sftp_append32(&state->txp, u);
}
static bool
check_state(SFTP_STATIC_TYPE state)
{
assert(state);
if (!state)
return false;
return true;
}
static bool
appendheader(SFTP_STATIC_TYPE state, uint8_t type)
{
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
enter_function(SFTP_STATIC_TYPE state)
{
if (!check_state(state))
return false;
if (pthread_mutex_lock(&state->mtx))
return false;
if (state->terminating) {
pthread_mutex_unlock(&state->mtx);
return false;
}
state->running++;
return true;
}
static bool
exit_function(SFTP_STATIC_TYPE state, bool retval)
{
assert(state->running > 0);
state->running--;
pthread_mutex_unlock(&state->mtx);
return retval;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment