]> git.sesse.net Git - betaftpd/blobdiff - ftpd.c
Fixed a gcc (2.97) warning.
[betaftpd] / ftpd.c
diff --git a/ftpd.c b/ftpd.c
index 473624e53ba55e8a115e8fdb3798dc343b9fe448..d7f460621d8013a9d6cc08811093583e95cc4af0 100644 (file)
--- 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
     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,
     License as published by the Free Software Foundation.
 
     This program is distributed in the hope that it will be useful,
 #include <config.h>
 #endif
 
 #include <config.h>
 #endif
 
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
 #if HAVE_ERRNO_H
 #include <errno.h>
 #endif
 #if HAVE_ERRNO_H
 #include <errno.h>
 #endif
 #include <unistd.h>
 #endif
 
 #include <unistd.h>
 #endif
 
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
 #if HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
 #if HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
 #include <sys/stat.h>
 #endif
 
 #include <sys/stat.h>
 #endif
 
-#if HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
 #if HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
 #if HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
 #include <linux/socket.h>
 #endif
 
 #include <linux/socket.h>
 #endif
 
+#if HAVE_LINUX_TCP_H
+#include <linux/tcp.h>
+#endif
+
 #if HAVE_MMAP
 #include <sys/mman.h>
 #endif
 #if HAVE_MMAP
 #include <sys/mman.h>
 #endif
 #include <ascii.h>
 #endif
 
 #include <ascii.h>
 #endif
 
-/* Debug printing -- remove before 0.0.8 final */
-#if 1
-#define DPRINT(s)
-#else
-#define DPRINT(s) printf s 
+#if WANT_DCACHE
+#include <dcache.h>
 #endif
 
 #ifndef MAP_FAILED
 #endif
 
 #ifndef MAP_FAILED
@@ -205,6 +194,10 @@ fd_set master_fds, master_send_fds;
 FILE *xferlog = NULL;
 #endif
 
 FILE *xferlog = NULL;
 #endif
 
+#if HAVE_LINUX_SENDFILE
+int sendfile_supported = 1;
+#endif
+
 /*
  * This variable specifies if it's soon time to check for timed out
  * clients, and timed out directory listing cache entries. It is
 /*
  * This variable specifies if it's soon time to check for timed out
  * clients, and timed out directory listing cache entries. It is
@@ -262,7 +255,6 @@ int vsnprintf(char *str, size_t n, const char *format, va_list ap)
  */
 int add_fd(const int fd, const int events)
 {
  */
 int add_fd(const int fd, const int events)
 {
-       DPRINT(("add_fd(%d, %x)\n", fd, events));
 #if HAVE_POLL
        if (fd >= FD_MAX) {
                printf("add_fd(%d, %x): failed\n", fd, events);
 #if HAVE_POLL
        if (fd >= FD_MAX) {
                printf("add_fd(%d, %x): failed\n", fd, events);
@@ -289,7 +281,6 @@ int add_fd(const int fd, const int events)
  */
 void del_fd(const int fd)
 {
  */
 void del_fd(const int fd)
 {
-       DPRINT(("del_fd(%d)\n", fd));
 #if HAVE_POLL
        if (fd >= FD_MAX)
                return;
 #if HAVE_POLL
        if (fd >= FD_MAX)
                return;
@@ -365,7 +356,6 @@ struct conn *alloc_new_conn(const int sock)
 {
        const unsigned int one = 1;
        struct conn *c = (struct conn *)(malloc(sizeof(struct conn)));
 {
        const unsigned int one = 1;
        struct conn *c = (struct conn *)(malloc(sizeof(struct conn)));
-       DPRINT(("alloc_new_conn(%d)\n", sock));
 
        if (c == NULL) return c;
 
 
        if (c == NULL) return c;
 
@@ -449,30 +439,6 @@ struct ftran *alloc_new_ftran(const int sock, const struct conn * const c)
        return f;
 }
 
        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
 /*
  * destroy_conn():
  *             Destroy a control connection, remove it from the linked
@@ -480,14 +446,11 @@ struct dcache *alloc_new_dcache()
  */
 void destroy_conn(struct conn * const c)
 {
  */
 void destroy_conn(struct conn * const c)
 {
-       DPRINT(("destroy_conn:\n"));
        if (c == NULL) return;
        del_fd(c->sock);
 
        destroy_ftran(c->transfer);
        remove_from_linked_list((struct list_element *)c);
        if (c == NULL) return;
        del_fd(c->sock);
 
        destroy_ftran(c->transfer);
        remove_from_linked_list((struct list_element *)c);
-
-       DPRINT(("destroy_conn done.\n"));
 }
 
 /*
 }
 
 /*
@@ -547,30 +510,9 @@ void destroy_ftran(struct ftran * const f)
        remove_from_linked_list((struct list_element *)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():
 /*
  * process_all_clients():
- *             Processes all the control connections in active_clients
+ *             Processes all the _control_ connections in active_clients
  *             (normally returned from a select(), there are at max
  *             NUM_AC active connections in the set), sending them
  *             through to the command parser if a command has been
  *             (normally returned from a select(), there are at max
  *             NUM_AC active connections in the set), sending them
  *             through to the command parser if a command has been
@@ -585,8 +527,6 @@ int process_all_clients(const fd_set * const active_clients, const int num_ac)
        struct conn *c = NULL, *next = first_conn->next_conn;
        int checked_through = 0;
 
        struct conn *c = NULL, *next = first_conn->next_conn;
        int checked_through = 0;
 
-       DPRINT(("process_all_clients: num_ac %d\n", num_ac));
-
        /* run through the linked list */
        while (next != NULL && checked_through < num_ac) {
                int bytes_avail;
        /* run through the linked list */
        while (next != NULL && checked_through < num_ac) {
                int bytes_avail;
@@ -594,14 +534,9 @@ int process_all_clients(const fd_set * const active_clients, const int num_ac)
                c = next;
                next = c->next_conn;
 #if HAVE_POLL
                c = next;
                next = c->next_conn;
 #if HAVE_POLL
-               if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) {
-                       destroy_conn(c);
-                       continue;
-               }
-               if (!fds[c->sock].revents & POLLIN) {
+               if ((fds[c->sock].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL)) == 0) {
                        continue;
                }
                        continue;
                }
-               DPRINT(("process_all_clients: fd %d has POLLIN set\n", c->sock));
 #else
                if (!FD_ISSET(c->sock, active_clients)) {
                        continue;
 #else
                if (!FD_ISSET(c->sock, active_clients)) {
                        continue;
@@ -619,8 +554,10 @@ int process_all_clients(const fd_set * const active_clients, const int num_ac)
                         * client has closed the socket. If we get a return value
                         * of -1 (error), we close the socket ourselves.
                         *
                         * client has closed the socket. If we get a return value
                         * of -1 (error), we close the socket ourselves.
                         *
-                        * Just to be safe, we include this code for poll() as
-                        * well.
+                        * We do the same for poll(), even though we actually have
+                        * bits that tell us what is happening (in case of new 
+                        * input AND error/hangup at the same time, we do an
+                        * explicit check at the bottom of the loop as well).
                         */
                        destroy_conn(c);
                        continue;
                         */
                        destroy_conn(c);
                        continue;
@@ -635,10 +572,37 @@ int process_all_clients(const fd_set * const active_clients, const int num_ac)
 
                c->buf_len += bytes_avail;
                parse_command(c);
 
                c->buf_len += bytes_avail;
                parse_command(c);
+
+               if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) {
+                        destroy_conn(c);
+                }
        }
        return checked_through;
 }
 
        }
        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)
+{
+       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);
+#if WANT_FULLSCREEN
+       update_display(first_conn);
+#endif
+}
+
 /*
  * process_all_sendfiles():
  *             Sends data to all clients that are ready to receive it.
 /*
  * process_all_sendfiles():
  *             Sends data to all clients that are ready to receive it.
@@ -660,8 +624,15 @@ int process_all_sendfiles(fd_set * const active_clients, const int num_ac)
                f = next;
                next = f->next_ftran;
 
                f = next;
                next = f->next_ftran;
 
+#if HAVE_UPLOAD
+               if (f->upload == 1 && fds[f->sock].revents & POLLHUP) {
+                       finish_transfer(f);
+                       continue;
+               }
+#endif
+
 #if HAVE_POLL
 #if HAVE_POLL
-               if (fds[f->sock].revents & (POLLHUP|POLLERR|POLLNVAL)) {
+               if (fds[f->sock].revents & (POLLERR|POLLNVAL|POLLHUP)) {
                        destroy_ftran(f);
                        continue;
                }
                        destroy_ftran(f);
                        continue;
                }
@@ -669,7 +640,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
 
                /* 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
 #else
                if ((f->state < 2) || (f->state == 3) || !FD_ISSET(f->sock, active_clients)) {
 #endif
@@ -719,16 +690,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 */
                        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
 #if WANT_FULLSCREEN
                 update_display(first_conn);
 #endif
@@ -741,7 +703,7 @@ int process_all_sendfiles(fd_set * const active_clients, const int num_ac)
 int do_upload(struct ftran *f)
 {
        char upload_buf[16384];
 int do_upload(struct ftran *f)
 {
        char upload_buf[16384];
-       int avail, size;
+       int size;
 #if WANT_ASCII
        /* keep buffer size small in ascii transfers 
           to prevent process stalling while filtering
 #if WANT_ASCII
        /* keep buffer size small in ascii transfers 
           to prevent process stalling while filtering
@@ -804,11 +766,7 @@ int do_download(struct ftran *f)
         * Here we use a rather simplified sending `algorithm',
         * leaving most of the quirks to the system calls.
         */
         * Here we use a rather simplified sending `algorithm',
         * leaving most of the quirks to the system calls.
         */
-       if (f->dir_listing == 0
-#if WANT_UPLOAD
-               && f->upload == 0
-#endif
-       ) {
+       if (sendfile_supported == 1 && f->dir_listing == 0) {
                int err;
                size = f->size - f->pos;
 
                int err;
                size = f->size - f->pos;
 
@@ -1015,6 +973,19 @@ int main(void)
        alarm(60);
        signal(SIGALRM, handle_alarm);
 
        alarm(60);
        signal(SIGALRM, handle_alarm);
 
+#if HAVE_LINUX_SENDFILE
+       /* check that sendfile() is really implemented (same check as configure does) */
+       {
+               int out_fd = 1, in_fd = 0;
+               off_t offset = 0;
+               size_t size = 1024;
+
+               errno = 0;
+               sendfile(out_fd, in_fd, &offset, size);
+               if (errno == ENOSYS) sendfile_supported = 0;
+       }
+#endif
+
        for ( ;; ) {
                int i;
 #ifndef HAVE_POLL
        for ( ;; ) {
                int i;
 #ifndef HAVE_POLL
@@ -1029,7 +1000,6 @@ int main(void)
 
 #if HAVE_POLL
                i = poll(fds, highest_fds + 1, 60000);
 
 #if HAVE_POLL
                i = poll(fds, highest_fds + 1, 60000);
-               DPRINT(("poll returns %d\n", i));
 #if 0
                {
                        int j;
 #if 0
                {
                        int j;
@@ -1193,29 +1163,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
 /*
  * remove_bytes():
  *             Remove some bytes from the incoming buffer. This gives
@@ -1252,7 +1199,6 @@ void numeric(struct conn * const c, const int numeric, const char * const format
        i = vsnprintf(buf, 256, fmt, args);
        va_end(args);
 
        i = vsnprintf(buf, 256, fmt, args);
        va_end(args);
 
-       DPRINT((buf));
        err = send(c->sock, buf, i, 0);
        if (err == -1 && errno == EPIPE) {
                destroy_conn(c);
        err = send(c->sock, buf, i, 0);
        if (err == -1 && errno == EPIPE) {
                destroy_conn(c);
@@ -1393,7 +1339,7 @@ void init_file_transfer(struct ftran * const f)
 #if HAVE_MMAP
        if (f->dir_listing == 0) {
 #if HAVE_LINUX_SENDFILE
 #if HAVE_MMAP
        if (f->dir_listing == 0) {
 #if HAVE_LINUX_SENDFILE
-               int do_mmap = 0;
+               int do_mmap = (sendfile_supported) ? 0 : 1;
 #else
                int do_mmap = 1;
 #endif
 #else
                int do_mmap = 1;
 #endif
@@ -1540,11 +1486,12 @@ void clear_bad_fds(int * const server_sock)
  * dump_file(): Dumps a file on the control connection. Used for
  *             welcome messages and the likes. Note that outbuf
  *             is so big, to prevent any crashing from users creating
  * dump_file(): Dumps a file on the control connection. Used for
  *             welcome messages and the likes. Note that outbuf
  *             is so big, to prevent any crashing from users creating
- *             weird .message files (like 1024 LFs)...
+ *             weird .message files (like 1024 LFs)... The size of
+ *             the file is limited to 1024 bytes (by truncation).
  */
 void dump_file(struct conn * const c, const int num, const char * const filename)
 {
  */
 void dump_file(struct conn * const c, const int num, const char * const filename)
 {
-       char buf[1024], outbuf[8192];
+       char buf[1024], outbuf[5121];
        char *ptr = outbuf + 4;
        int i, j = -1;
 
        char *ptr = outbuf + 4;
        int i, j = -1;
 
@@ -1583,28 +1530,26 @@ void list_readmes(struct conn * const c)
        const time_t now = time(NULL);
        int i;
 
        const time_t now = time(NULL);
        int i;
 
-       if (glob("README*", 0, NULL, &pglob) == 0) {
-               for (i = 0; i < pglob.gl_pathc; i++) {
-                       const char * const temp = pglob.gl_pathv[i];
-                       struct stat buf;
-                       char str[2048];
+       if (glob("README*", 0, NULL, &pglob) != 0) return;
 
 
-                       char *tm;
+       for (i = 0; i < pglob.gl_pathc; i++) {
+               struct stat buf;
+               char str[256];
+               char *tm;
 
 
-                       if (stat(temp, &buf) == -1) continue;
+               if (stat(pglob.gl_pathv[i], &buf) == -1) continue;
 
 
-                       /* remove trailing LF */
-                       tm = ctime(&buf.st_mtime);
-                       tm[strlen(tm) - 1] = 0;
+               /* remove trailing LF */
+               tm = ctime(&buf.st_mtime);
+               tm[strlen(tm) - 1] = 0;
 
 
-                       sprintf(str, "250-Please read the file %s\r\n"
-                                    "250-\tIt was last modified %s - %ld days ago\r\n",
-                               temp, tm,
-                               (now - buf.st_mtime) / 86400);
-                       send(c->sock, str, strlen(str), 0);
-               }
-               globfree(&pglob);
-       }       
+               snprintf(str, 256, "250-Please read the file %s\r\n"
+                                  "250-\tIt was last modified %s - %ld days ago\r\n",
+                       pglob.gl_pathv[i], tm,
+                       (now - buf.st_mtime) / 86400);
+               send(c->sock, str, strlen(str), 0);
+       }
+       globfree(&pglob);
 }
 #endif
 
 }
 #endif