From a606b2eade34c3d8e662e94a32af960b5d2911f1 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sat, 21 Jan 2023 14:09:56 +0200 Subject: [PATCH] foo --- src/ftp.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- src/ftp.h | 30 ++++- 2 files changed, 387 insertions(+), 35 deletions(-) diff --git a/src/ftp.c b/src/ftp.c index 961eea3..6a29a77 100644 --- a/src/ftp.c +++ b/src/ftp.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -19,13 +20,20 @@ #define fcntlsocket fcntl #endif +#define TIMEOUT 5 + +static int newconn(struct ftp *ftp); static int sendcmd(struct ftp *ftp, const char *fmt, ...); static int handle_control(struct ftp *ftp); static int handle_data(struct ftp *ftp, int s); static void proc_control(struct ftp *ftp, const char *buf); -static void cproc_pwd(struct ftp *ftp, int code, const char *buf, void *cls); -static void cproc_cwd(struct ftp *ftp, int code, const char *buf, void *cls); +static int cproc_active(struct ftp *ftp, int code, const char *buf, void *cls); +static int cproc_pwd(struct ftp *ftp, int code, const char *buf, void *cls); +static int cproc_cwd(struct ftp *ftp, int code, const char *buf, void *cls); + +static int cproc_list(struct ftp *ftp, int code, const char *buf, void *cls); +static void dproc_list(struct ftp *ftp, const char *buf, int sz, void *cls); struct ftp *ftp_alloc(void) @@ -35,7 +43,7 @@ struct ftp *ftp_alloc(void) if(!(ftp = calloc(1, sizeof *ftp))) { return 0; } - ftp->ctl = ftp->data = -1; + ftp->ctl = ftp->data = ftp->lis = -1; return ftp; } @@ -89,40 +97,205 @@ void ftp_close(struct ftp *ftp) ftp->ctl = -1; ftp->num_crecv = 0; } + if(ftp->lis >= 0) { + closesocket(ftp->lis); + ftp->lis = -1; + } if(ftp->data >= 0) { closesocket(ftp->data); ftp->data = -1; - ftp->num_drecv = 0; } } int ftp_sockets(struct ftp *ftp, int *sockv, int maxsize) { - if(ftp->ctl >= 0 && maxsize) { - *sockv++ = ftp->ctl; - maxsize--; + int *sptr = sockv; + if(ftp->ctl >= 0 && maxsize-- > 0) { + *sptr++ = ftp->ctl; } - return 1; + if(ftp->lis >= 0 && maxsize-- > 0) { + *sptr++ = ftp->lis; + } + if(ftp->data >= 0 && maxsize-- > 0) { + *sptr++ = ftp->data; + } + return sptr - sockv; } -int ftp_pending(struct ftp *ftp) +static void exec_op(struct ftp *ftp, int op, const char *arg) +{ + switch(op) { + case FTP_PWD: + ftp_pwd(ftp); + break; + + case FTP_CHDIR: + ftp_chdir(ftp, arg); + break; + + case FTP_MKDIR: + ftp_mkdir(ftp, arg); + break; + + case FTP_RMDIR: + ftp_rmdir(ftp, arg); + break; + + case FTP_DEL: + ftp_delete(ftp, arg); + break; + + case FTP_LIST: + ftp_list(ftp); + break; + + case FTP_RETR: + ftp_retrieve(ftp, arg); + break; + + case FTP_STORE: + ftp_store(ftp, arg); + break; + + default: + break; + } +} + +static void exec_queued(struct ftp *ftp) +{ + struct ftp_op *fop; + + if(!(fop = ftp->qhead)) { + return; + } + + if(ftp->qtail == fop) { + ftp->qhead = ftp->qtail = 0; + } else { + ftp->qhead = ftp->qhead->next; + } + + exec_op(ftp, fop->op, fop->arg); + + free(fop->arg); + free(fop); +} + + +int ftp_queue(struct ftp *ftp, int op, const char *arg) +{ + struct ftp_op *fop; + + if(!ftp->qhead) { + exec_op(ftp, op, arg); + return 0; + } + + if(!(fop = malloc(sizeof *fop))) { + return -1; + } + if(!(fop->arg = strdup(arg))) { + free(fop); + return -1; + } + fop->op = op; + fop->next = 0; + + ftp->qtail->next = fop; + ftp->qtail = fop; + return 0; +} + +int ftp_waitresp(struct ftp *ftp, time_t timeout) { - int maxfd = -1; fd_set rdset; - struct timeval tv = {0, 0}; + struct timeval tv; + time_t start; - FD_ZERO(&rdset); - if(ftp->ctl >= 0) { + ftp->last_resp = -1; + start = time(0); + + for(;;) { + FD_ZERO(&rdset); FD_SET(ftp->ctl, &rdset); - maxfd = ftp->ctl; + + if(timeout >= 0) { + tv.tv_sec = timeout; + tv.tv_usec = 0; + } + + if(select(ftp->ctl + 1, &rdset, 0, 0, timeout >= 0 ? &tv : 0) == -1 && errno == EINTR) { + continue; + } + + if(FD_ISSET(ftp->ctl, &rdset)) { + ftp->last_resp = -1; + ftp_handle(ftp, ftp->ctl); + if(ftp->last_resp) { + break; + } + } + + if(timeout > 0) { + timeout -= time(0) - start; + if(timeout <= 0) { + return -1; + } + } + + } + return ftp->last_resp; +} + +static int ftp_active(struct ftp *ftp) +{ + struct sockaddr_in sa = {0}; + socklen_t len; + unsigned long addr; + unsigned short port; + + if(ftp->lis >= 0) { + closesocket(ftp->lis); } if(ftp->data >= 0) { - FD_SET(ftp->data, &rdset); - if(ftp->data > maxfd) maxfd = ftp->data; + closesocket(ftp->data); + ftp->data = -1; + } + + if((ftp->lis = socket(PF_INET, SOCK_STREAM, 0)) == -1) { + errmsg("ftp_active: failed to create listening socket\n"); + return -1; + } + + sa.sin_family = AF_INET; + sa.sin_port = 0; + sa.sin_addr.s_addr = htonl(INADDR_ANY); + + if(bind(ftp->lis, (struct sockaddr*)&sa, sizeof sa) == -1) { + errmsg("ftp_active: failed to bind listening socket\n"); + closesocket(ftp->lis); + ftp->lis = -1; + return -1; } - if(maxfd == -1) return 0; + listen(ftp->lis, 1); - return select(maxfd + 1, &rdset, 0, 0, &tv) > 0 ? 1 : 0; + len = sizeof sa; + if(getsockname(ftp->ctl, (struct sockaddr*)&sa, &len) == -1) { + errmsg("ftp_active: failed to retrieve listening socket address\n"); + closesocket(ftp->lis); + ftp->lis = -1; + return -1; + } + + addr = ntohl(sa.sin_addr.s_addr); + port = ntohs(sa.sin_port); + + sendcmd(ftp, "PORT %d,%d,%d,%d,%d,%d", addr >> 24, (addr >> 16) & 0xff, + (addr >> 8) & 0xff, addr & 0xff, port >> 8, port & 0xff); + + ftp->cproc = cproc_active; + return 0; } int ftp_handle(struct ftp *ftp, int s) @@ -133,6 +306,15 @@ int ftp_handle(struct ftp *ftp, int s) if(s == ftp->data) { return handle_data(ftp, s); } + if(s == ftp->lis) { + int ns = accept(s, 0, 0); + if(ftp->data >= 0) { + closesocket(ns); + } else { + ftp->data = ns; + } + return 0; + } return -1; } @@ -189,7 +371,33 @@ static int handle_control(struct ftp *ftp) static int handle_data(struct ftp *ftp, int s) { - return -1; + int rd; + + if(ftp->data == -1) { + return -1; + } + + for(;;) { + if((rd = recv(ftp->data, ftp->drecv, sizeof ftp->drecv, 0)) == -1) { + if(errno == EINTR) continue; + /* assume EWOULDBLOCK, try again next time */ + break; + } + + /* call the callback first, so that we'll get a 0-count call to indicate + * EOF when the server closes the data connection + */ + if(ftp->dproc) { + ftp->dproc(ftp, ftp->drecv, rd, ftp->dproc_cls); + } + + if(rd == 0) { + closesocket(ftp->data); + ftp->data = -1; + return -1; + } + } + return 0; } static int respcode(const char *resp) @@ -221,9 +429,15 @@ static void proc_control(struct ftp *ftp, const char *buf) return; /* ignore continuations for now */ } + ftp->last_resp = code; + if(ftp->cproc) { - ftp->cproc(ftp, code, buf, ftp->cproc_cls); - ftp->cproc = 0; + if(ftp->cproc(ftp, code, buf, ftp->cproc_cls) <= 0) { + ftp->cproc = 0; + + /* execute next operation if there's one queued */ + exec_queued(ftp); + } return; } @@ -235,10 +449,10 @@ static void proc_control(struct ftp *ftp, const char *buf) sendcmd(ftp, "pass %s", ftp->pass ? ftp->pass : "foobar"); break; case 230: - ftp->status = 1; infomsg("login successful\n"); - ftp_pwd(ftp); - ftp->modified = 1; + if(newconn(ftp) == -1) { + ftp_close(ftp); + } break; case 530: ftp->status = 0; @@ -247,6 +461,16 @@ static void proc_control(struct ftp *ftp, const char *buf) } } +static int newconn(struct ftp *ftp) +{ + if(ftp_active(ftp) == -1) { + return -1; + } + ftp_queue(ftp, FTP_PWD, 0); + ftp_queue(ftp, FTP_LIST, 0); + return 0; +} + int ftp_update(struct ftp *ftp) { return -1; @@ -261,12 +485,64 @@ int ftp_pwd(struct ftp *ftp) int ftp_chdir(struct ftp *ftp, const char *dirname) { - // TODO queue sendcmd(ftp, "cwd %s", dirname); ftp->cproc = cproc_cwd; return 0; } +int ftp_mkdir(struct ftp *ftp, const char *dirname) +{ + return -1; +} + +int ftp_rmdir(struct ftp *ftp, const char *dirname) +{ + return -1; +} + +int ftp_delete(struct ftp *ftp, const char *fname) +{ + return -1; +} + +struct recvbuf { + char *buf; + long size, bufsz; +}; + +int ftp_list(struct ftp *ftp) +{ + struct recvbuf *rbuf; + + if(!(rbuf = malloc(sizeof *rbuf))) { + errmsg("failed to allocate receive buffer\n"); + return -1; + } + rbuf->size = 0; + rbuf->bufsz = 1024; + if(!(rbuf->buf = malloc(rbuf->bufsz))) { + free(rbuf); + errmsg("failed to allocate receive buffer\n"); + return -1; + } + + sendcmd(ftp, "list"); + ftp->cproc = cproc_list; + ftp->dproc = dproc_list; + ftp->cproc_cls = ftp->dproc_cls = rbuf; + return 0; +} + +int ftp_retrieve(struct ftp *ftp, const char *fname) +{ + return -1; +} + +int ftp_store(struct ftp *ftp, const char *fname) +{ + return -1; +} + static int get_quoted_text(const char *str, char *buf) { int len; @@ -286,26 +562,84 @@ static int get_quoted_text(const char *str, char *buf) return 0; } -static void cproc_pwd(struct ftp *ftp, int code, const char *buf, void *cls) +static int cproc_active(struct ftp *ftp, int code, const char *buf, void *cls) +{ + if(code != 200) { + errmsg("ftp_active failed\n"); + ftp_close(ftp); + } else { + ftp->status = FTP_CONN_ACT; + } + return 0; +} + +static int cproc_pwd(struct ftp *ftp, int code, const char *buf, void *cls) { char *dirname; if(code != 257) { warnmsg("pwd failed\n"); - return; + return -1; } dirname = alloca(strlen(buf) + 1); if(get_quoted_text(buf, dirname) == -1) { warnmsg("pwd: invalid response: %s\n", buf); - return; + return -1; } free(ftp->curdir_rem); ftp->curdir_rem = strdup_nf(dirname); ftp->modified = 1; + return 0; +} + +static int cproc_cwd(struct ftp *ftp, int code, const char *buf, void *cls) +{ + return -1; +} + +static int cproc_list(struct ftp *ftp, int code, const char *buf, void *cls) +{ + if(code < 200) { + /* expect more */ + return 1; + } + + if(code >= 400) { + errmsg("failed to retrieve directory listing\n"); + } + return 0; } -static void cproc_cwd(struct ftp *ftp, int code, const char *buf, void *cls) +static void dproc_list(struct ftp *ftp, const char *buf, int sz, void *cls) { + struct recvbuf *rbuf = cls; + + if(sz == 0) { + /* EOF condition, we got the whole list, update directory entries */ + /* TODO */ + rbuf->buf[rbuf->size] = 0; + fprintf(stderr, "%s\n", rbuf->buf); + + free(rbuf->buf); + free(rbuf); + ftp->dproc = 0; + return; + } + + if(rbuf->size + sz > rbuf->bufsz) { + char *tmp; + int newsz = rbuf->bufsz << 1; + + if(!(tmp = realloc(rbuf->buf, newsz))) { + errmsg("failed to resize receive buffer\n"); + return; + } + rbuf->buf = tmp; + rbuf->bufsz = newsz; + } + + memcpy(rbuf->buf + rbuf->size, buf, sz); + rbuf->size += sz; } diff --git a/src/ftp.h b/src/ftp.h index c9ebbe2..e8bcf10 100644 --- a/src/ftp.h +++ b/src/ftp.h @@ -14,9 +14,17 @@ enum { FTP_STORE }; +enum { + FTP_DISC, + FTP_CONN_PASV, + FTP_CONN_ACT +}; + struct ftp_op { int op; char *arg; + + struct ftp_op *next; }; struct ftp_dirent { @@ -25,24 +33,26 @@ struct ftp_dirent { }; struct ftp { - int ctl, data; /* sockets */ + int ctl, lis, data; /* sockets */ + int lis_port; int status; char *user, *pass; - void (*cproc)(struct ftp *ftp, int code, const char *buf, void *cls); + struct ftp_op *qhead, *qtail; + + int (*cproc)(struct ftp *ftp, int code, const char *buf, void *cls); void (*dproc)(struct ftp *ftp, const char *buf, int sz, void *cls); void *cproc_cls, *dproc_cls; char crecv[256]; int num_crecv; char drecv[256]; - int num_drecv; char *curdir_rem, *curdir_loc; - struct ftp_dirent *dent; - int num_dent; + struct ftp_dirent *dent_rem, *dent_loc; + int last_resp; int modified; }; @@ -55,13 +65,21 @@ int ftp_connect(struct ftp *ftp, const char *host, int port); void ftp_close(struct ftp *ftp); int ftp_sockets(struct ftp *ftp, int *sockv, int maxsize); -int ftp_pending(struct ftp *ftp); int ftp_handle(struct ftp *ftp, int s); +int ftp_queue(struct ftp *ftp, int op, const char *arg); +int ftp_waitresp(struct ftp *ftp, time_t timeout); + int ftp_update(struct ftp *ftp); int ftp_pwd(struct ftp *ftp); int ftp_chdir(struct ftp *ftp, const char *dirname); +int ftp_mkdir(struct ftp *ftp, const char *dirname); +int ftp_rmdir(struct ftp *ftp, const char *dirname); +int ftp_delete(struct ftp *ftp, const char *fname); +int ftp_list(struct ftp *ftp); +int ftp_retrieve(struct ftp *ftp, const char *fname); +int ftp_store(struct ftp *ftp, const char *fname); #endif /* FTP_H_ */ -- 1.7.10.4