X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ftpd.c;h=f013777ec65dcb39a3660f5234baa79a143fe005;hb=refs%2Fheads%2Fmaster;hp=b35a9a04b1509d638204dd34061ba87c41420a6f;hpb=e94f8e0fde55d54693e6886729627c544dc772a5;p=betaftpd diff --git a/ftpd.c b/ftpd.c index b35a9a0..f013777 100644 --- a/ftpd.c +++ b/ftpd.c @@ -2,7 +2,7 @@ Copyright (C) 1999-2000 Steinar H. Gunderson This program is is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License, version 2 if the + it under the terms of the GNU General Public License, version 2 of the License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, @@ -28,10 +28,6 @@ #include #endif -#if HAVE_SYS_TYPES_H -#include -#endif - #if HAVE_ERRNO_H #include #endif @@ -40,6 +36,10 @@ #include #endif +#if HAVE_SYS_TYPES_H +#include +#endif + #if HAVE_SYS_CONF_H #include #endif @@ -76,10 +76,22 @@ #include #endif +#if HAVE_NETINET_IN_SYSTM_H +#include +#endif + #if HAVE_NETINET_IN_H #include #endif +#if HAVE_NETINET_IP_H +#include +#endif + +#if HAVE_NETINET_TCP_H +#include +#endif + #if HAVE_ARPA_INET_H #include #endif @@ -88,26 +100,10 @@ #include #endif -#if HAVE_SYS_SOCKET_H -#include -#endif - #if HAVE_SYS_IOCTL_H #include #endif -#if HAVE_NETINET_IN_SYSTM_H -#include -#endif - -#if HAVE_NETINET_IP_H -#include -#endif - -#if HAVE_NETINET_TCP_H -#include -#endif - #if HAVE_LINUX_SOCKET_H #include #endif @@ -175,6 +171,10 @@ #include #endif +#if WANT_DCACHE +#include +#endif + #ifndef MAP_FAILED #define MAP_FAILED -1 #endif @@ -184,6 +184,7 @@ struct ftran *first_ftran = NULL; #if WANT_DCACHE struct dcache *first_dcache = NULL; #endif +char message_buf[512]; #if HAVE_POLL unsigned int highest_fds = 0; @@ -202,7 +203,7 @@ fd_set master_fds, master_send_fds; FILE *xferlog = NULL; #endif -#if HAVE_LINUX_SENDFILE +#if HAVE_LINUX_SENDFILE || HAVE_BSD_SENDFILE int sendfile_supported = 1; #endif @@ -214,7 +215,7 @@ int sendfile_supported = 1; */ int time_to_check = 1; -#ifndef HAVE_SPRINTF +#ifndef HAVE_SNPRINTF /* * snprintf(): snprintf() replacement for systems that miss it. Note * that this implementation does _not_ necessarily protect @@ -365,7 +366,7 @@ struct conn *alloc_new_conn(const int sock) const unsigned int one = 1; struct conn *c = (struct conn *)(malloc(sizeof(struct conn))); - if (c == NULL) return c; + if (c == NULL) return NULL; if (sock != -1) { ioctl(sock, FIONBIO, &one); @@ -390,6 +391,7 @@ struct conn *alloc_new_conn(const int sock) #if WANT_ASCII c->ascii_mode = 0; #endif + c->free_me = 0; /* * equals: @@ -406,7 +408,7 @@ struct conn *alloc_new_conn(const int sock) time(&(c->last_transfer)); - /*list_clients();*/ + /* list_clients(); */ return c; } @@ -444,33 +446,12 @@ struct ftran *alloc_new_ftran(const int sock, const struct conn * const c) #endif f->dir_listing = 0; +#if WANT_UPLOAD + f->upload = 0; +#endif return f; } -#if WANT_DCACHE -/* - * alloc_new_dcache(): - * Allocates a new directory cache entry (type `struct dcache'), - * and adds it to the linked list. - */ -struct dcache *alloc_new_dcache() -{ - struct dcache *d = (struct dcache *)(malloc(sizeof(struct dcache))); - - if (d == NULL) return d; - - d->use_count = 0; - d->last_used = 0; - strcpy(d->dir_name, ""); - d->dir_data = NULL; - - add_to_linked_list((struct list_element *)first_dcache, - (struct list_element *)d); - - return d; -} -#endif - /* * destroy_conn(): * Destroy a control connection, remove it from the linked @@ -542,27 +523,6 @@ void destroy_ftran(struct ftran * const f) remove_from_linked_list((struct list_element *)f); } -#if WANT_DCACHE -/* - * destroy_dcache(): - * Destroy a directory listing cache entry, remove it from the - * linked list, and clean up after it. - * - * If you free a cache entry that is in use (use_count > 0), - * BetaFTPD will most likely crash (later). The thing you're supposed - * to do when you're done with a dcache entry, is to decrement - * its use_count, and let the timeout functions do the destroying - * when it's time to do so. - */ -void destroy_dcache(struct dcache * const d) -{ - if (d == NULL) return; - - if (d->dir_data != NULL) free(d->dir_data); - remove_from_linked_list((struct list_element *)d); -} -#endif - /* * process_all_clients(): * Processes all the _control_ connections in active_clients @@ -626,13 +586,41 @@ int process_all_clients(const fd_set * const active_clients, const int num_ac) c->buf_len += bytes_avail; parse_command(c); - if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) { + if (c->free_me || (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL))) { destroy_conn(c); } } return checked_through; } +/* + * finish_transfer(): + * Send a message that the transfer is completed, write xferlog + * entry (optional), and update the last_transfer record in the + * file transfer object. Goes for both uploads and downloads. + */ +void finish_transfer(struct ftran * const f) +{ + char finished[] = "226 Transfer complete.\r\n"; + if (send(f->owner->sock, finished, strlen(finished), 0) == -1 && errno == EPIPE) { + destroy_conn(f->owner); + return; + } + + time(&(f->owner->last_transfer)); + +#if WANT_XFERLOG + if (!f->dir_listing) { + write_xferlog(f); + } +#endif + + destroy_ftran(f); +#if WANT_FULLSCREEN + update_display(first_conn); +#endif +} + /* * process_all_sendfiles(): * Sends data to all clients that are ready to receive it. @@ -647,15 +635,21 @@ int process_all_sendfiles(fd_set * const active_clients, const int num_ac) { struct ftran *f = NULL, *next = first_ftran->next_ftran; int checked_through = 0; - struct sockaddr tempaddr; - int tempaddr_len = sizeof(tempaddr); + int tempaddr_len = sizeof(struct sockaddr_in); while (next != NULL && checked_through < num_ac) { f = next; next = f->next_ftran; +#if WANT_UPLOAD + if ((f->upload == 1) && (fds[f->sock].revents & POLLHUP)) { + finish_transfer(f); + continue; + } +#endif + #if HAVE_POLL - if (fds[f->sock].revents & (POLLHUP|POLLERR|POLLNVAL)) { + if (fds[f->sock].revents & (POLLERR|POLLNVAL|POLLHUP)) { destroy_ftran(f); continue; } @@ -663,7 +657,7 @@ int process_all_sendfiles(fd_set * const active_clients, const int num_ac) /* state = 2: incoming PASV, state >3: send file */ #if HAVE_POLL - if ((f->state < 2) || (f->state == 3) || (fds[f->sock].revents & (POLLIN|POLLOUT)) == 0) { + if ((f->state < 2) || (f->state == 3) || (fds[f->sock].revents & (POLLIN|POLLOUT)) == 0) { #else if ((f->state < 2) || (f->state == 3) || !FD_ISSET(f->sock, active_clients)) { #endif @@ -680,8 +674,7 @@ int process_all_sendfiles(fd_set * const active_clients, const int num_ac) if (f->state == 2) { /* incoming PASV */ const unsigned int one = 1; - const int tempsock = accept(f->sock, (struct sockaddr *)&tempaddr, - &tempaddr_len); + const int tempsock = accept(f->sock, &(f->sin), &tempaddr_len); del_fd(f->sock); @@ -693,12 +686,25 @@ int process_all_sendfiles(fd_set * const active_clients, const int num_ac) f->sock = tempsock; ioctl(f->sock, FIONBIO, &one); init_file_transfer(f); + + flush_numeric(f->owner); + if (f->owner->free_me) { + destroy_conn(f->owner); + continue; + } + #if WANT_UPLOAD if (f->upload) continue; #endif } if (f->state < 5) { init_file_transfer(f); + + flush_numeric(f->owner); + if (f->owner->free_me) { + destroy_conn(f->owner); + continue; + } #if WANT_UPLOAD if (f->upload) continue; #endif @@ -713,16 +719,7 @@ int process_all_sendfiles(fd_set * const active_clients, const int num_ac) if (do_download(f)) continue; /* do_{upload,download} returned 0, the transfer is complete */ - numeric(f->owner, 226, "Transfer complete."); - time(&(f->owner->last_transfer)); - -#if WANT_XFERLOG - if (!f->dir_listing) { - write_xferlog(f); - } -#endif - - destroy_ftran(f); + finish_transfer(f); #if WANT_FULLSCREEN update_display(first_conn); #endif @@ -765,7 +762,7 @@ int do_upload(struct ftran *f) #endif if (size > 0 && (write(f->local_file, upload_buf, size) == size)) { return 1; - } else if (size == -1) { + } else if (size == -1 && errno != EAGAIN) { /* don't write xferlog... or? */ numeric(f->owner, 426, strerror(errno)); destroy_ftran(f); @@ -792,7 +789,7 @@ int do_download(struct ftran *f) #endif int size; -#if HAVE_LINUX_SENDFILE +#if HAVE_LINUX_SENDFILE || HAVE_BSD_SENDFILE /* * We handle the optimal case first, which is sendfile(). * Here we use a rather simplified sending `algorithm', @@ -811,7 +808,7 @@ int do_download(struct ftran *f) } #endif - err = sendfile(f->sock, f->local_file, &f->pos, size); + err = mysendfile(f->sock, f->local_file, &f->pos, size); return (f->pos < f->size) && (err > -1); } #endif @@ -871,11 +868,11 @@ void write_xferlog(struct ftran *f) if (xferlog == NULL) return; - strftime(temp, 256, "%a %b %d %H:%M:%S %Y", t); + strftime(temp, 256, "%a %b %d %H:%M:%S %Y", t); #if WANT_UPLOAD - fprintf(xferlog, "%s %u %s %lu %s b _ %c a %s ftp 0 * \n", + fprintf(xferlog, "%s %u %s %lu %s b _ %c %c %s ftp 0 *\n", #else - fprintf(xferlog, "%s %u %s %lu %s b _ o a %s ftp 0 *\n", + fprintf(xferlog, "%s %u %s %lu %s b _ o %c %s ftp 0 *\n", #endif temp, (int)(difftime(now, f->tran_start)), inet_ntoa(f->sin.sin_addr), f->size, @@ -883,7 +880,7 @@ void write_xferlog(struct ftran *f) #if WANT_UPLOAD (f->upload) ? 'i' : 'o', #endif - f->owner->username); + (f->owner->auth == 4) ? 'r' : 'a', f->owner->username); fflush(xferlog); #if 0 @@ -976,8 +973,8 @@ int main(void) #warning No xferlog support for nonroot yet #else /* open xferlog */ - xferlog = fopen("/var/log/xferlog", "r+"); - if (xferlog == NULL) xferlog = fopen("/usr/adm/xferlog", "r+"); + xferlog = fopen("/var/log/xferlog", "a"); + if (xferlog == NULL) xferlog = fopen("/usr/adm/xferlog", "a"); if (xferlog != NULL) { fseek(xferlog, 0L, SEEK_END); @@ -1005,7 +1002,7 @@ int main(void) alarm(60); signal(SIGALRM, handle_alarm); -#if HAVE_LINUX_SENDFILE +#if HAVE_LINUX_SENDFILE || HAVE_BSD_SENDFILE /* check that sendfile() is really implemented (same check as configure does) */ { int out_fd = 1, in_fd = 0; @@ -1013,7 +1010,7 @@ int main(void) size_t size = 1024; errno = 0; - sendfile(out_fd, in_fd, &offset, size); + mysendfile(out_fd, in_fd, &offset, size); if (errno == ENOSYS) sendfile_supported = 0; } #endif @@ -1143,10 +1140,14 @@ void accept_new_client(int * const server_sock) struct conn * const c = alloc_new_conn(tempsock); num_err = 0; if (c != NULL) { - numeric(c, 220, "BetaFTPD " VERSION " ready."); + char hello[] = "220 BetaFTPD " VERSION " ready.\r\n"; + #if WANT_STAT memcpy(&(c->addr), &tempaddr, sizeof(struct sockaddr)); #endif + + if (send(tempsock, hello, strlen(hello), 0) == -1 && errno == EPIPE) + destroy_conn(c); } } } @@ -1195,29 +1196,6 @@ void time_out_sockets() } } -#if WANT_DCACHE -/* - * time_out_dcache(): - * Time out expired directory listing cache entries. - * Uses much of the same code as time_out_sockets(). - */ -void time_out_dcache() -{ - struct dcache *d = NULL, *next = first_dcache->next_dcache; - time_t now = time(NULL); - - /* run through the linked list */ - while (next != NULL) { - d = next; - next = d->next_dcache; - - if (d->use_count == 0 && (now - d->last_used > 900)) { - destroy_dcache(d); - } - } -} -#endif - /* * remove_bytes(): * Remove some bytes from the incoming buffer. This gives @@ -1241,23 +1219,36 @@ void remove_bytes(struct conn * const c, const int num) * you can use this command much the same way as you * would use a printf() (with all the normal %s, %d, * etc.), since it actually uses printf() internally. + * + * This command doesn't actually SEND the data -- it + * just puts it in a buffer which is sent after the + * command handler has completed. The reasons for this + * are simple -- it makes error checking and cleanup + * MUCH cleaner, so we won't have to check for errors + * in every single little branch of the code. */ void numeric(struct conn * const c, const int numeric, const char * const format, ...) { - char buf[256], fmt[256]; + char fmt[256]; va_list args; - int i, err; + int i; + int in_buf = strlen(message_buf); snprintf(fmt, 256, "%03u %s\r\n", numeric, format); va_start(args, format); - i = vsnprintf(buf, 256, fmt, args); + i = vsnprintf(message_buf + in_buf, 512 - in_buf, fmt, args); va_end(args); +} - err = send(c->sock, buf, i, 0); - if (err == -1 && errno == EPIPE) { - destroy_conn(c); - } +/* flush_numeric(): + * Actually flushes the buffer written by numeric() -- but does + * NOT erase it. If an error, sets the "free_me" flag in the socket. + */ +void flush_numeric(struct conn * const c) +{ + if (send(c->sock, message_buf, strlen(message_buf), 0) == -1 && errno == EPIPE) + c->free_me = 1; } /* @@ -1393,7 +1384,7 @@ void init_file_transfer(struct ftran * const f) */ #if HAVE_MMAP if (f->dir_listing == 0) { -#if HAVE_LINUX_SENDFILE +#if HAVE_LINUX_SENDFILE || HAVE_BSD_SENDFILE int do_mmap = (sendfile_supported) ? 0 : 1; #else int do_mmap = 1; @@ -1536,6 +1527,24 @@ void clear_bad_fds(int * const server_sock) } #endif +#if HAVE_BSD_SENDFILE || HAVE_LINUX_SENDFILE +int mysendfile(int sock, int fd, off_t *offset, size_t count) +{ +#if HAVE_BSD_SENDFILE + int err; + off_t ssize = 0; + + err = sendfile(fd, sock, *offset, count, NULL, &ssize, 0); + if (ssize > 0) *offset += ssize; +#else /* !HAVE_BSD_SENDFILE */ +#if HAVE_LINUX_SENDFILE + return sendfile(sock, fd, offset, count); +#endif /* HAVE_LINUX_SENDFILE */ +#endif /* !HAVE_BSD_SENDFILE */ +} +#endif /* HAVE_BSD_SENDFILE || HAVE_LINUX_SENDFILE */ + + #if WANT_MESSAGE /* * dump_file(): Dumps a file on the control connection. Used for