]> sjero.net Git - wget/blobdiff - src/ftp.c
[svn] Imported Mauro's IPv6 changes.
[wget] / src / ftp.c
index d99ddb71db43a589ef6dc93d04bb7d090c666478..7c0bee7b3e873fedcf809e42bd8e9758a7c9efce 100644 (file)
--- a/src/ftp.c
+++ b/src/ftp.c
@@ -52,7 +52,6 @@ so, delete this exception statement from your version.  */
 #include "ftp.h"
 #include "connect.h"
 #include "host.h"
-#include "fnmatch.h"
 #include "netrc.h"
 #include "convert.h"           /* for downloaded_file */
 
@@ -60,6 +59,8 @@ so, delete this exception statement from your version.  */
 extern int errno;
 #endif
 
+extern LARGE_INT total_downloaded_bytes;
+
 /* File where the "ls -al" listing will be saved.  */
 #define LIST_FILENAME ".listing"
 
@@ -120,6 +121,110 @@ ftp_expected_bytes (const char *s)
   return res;
 }
 
+#ifdef ENABLE_IPV6
+static int
+getfamily (int fd)
+{
+  struct sockaddr_storage ss;
+  struct sockaddr *sa = (struct sockaddr *)&ss;
+  socklen_t len = sizeof (ss);
+
+  assert (fd >= 0);
+
+  if (getpeername (fd, sa, &len) < 0)
+    /* Mauro Tortonesi: HOW DO WE HANDLE THIS ERROR? */
+    abort ();
+
+  return sa->sa_family;
+}
+
+/* 
+ * This function sets up a passive data connection with the FTP server.
+ * It is merely a wrapper around ftp_epsv, ftp_lpsv and ftp_pasv.
+ */
+static uerr_t
+ftp_do_pasv (struct rbuf *rbuf, ip_address *addr, unsigned short *port)
+{
+  uerr_t err;
+  int family;
+
+  family = getfamily (rbuf->fd);
+  assert (family == AF_INET || family == AF_INET6);
+
+  /* If our control connection is over IPv6, then we first try EPSV and then 
+   * LPSV if the former is not supported. If the control connection is over 
+   * IPv4, we simply issue the good old PASV request. */
+  if (family == AF_INET6)
+    {
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> EPSV ... ");
+      err = ftp_epsv (rbuf, addr, port);
+
+      /* If EPSV is not supported try LPSV */
+      if (err == FTPNOPASV)
+        {
+          if (!opt.server_response)
+            logputs (LOG_VERBOSE, "==> LPSV ... ");
+          err = ftp_lpsv (rbuf, addr, port);
+        }
+    }
+  else 
+    {
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> PASV ... ");
+      err = ftp_pasv (rbuf, addr, port);
+    }
+
+  return err;
+}
+
+/* 
+ * This function sets up an active data connection with the FTP server.
+ * It is merely a wrapper around ftp_eprt, ftp_lprt and ftp_port.
+ */
+static uerr_t
+ftp_do_port (struct rbuf *rbuf)
+{
+  uerr_t err;
+  int family;
+
+  assert (rbuf != NULL);
+  assert (rbuf_initialized_p (rbuf));
+
+  family = getfamily (rbuf->fd);
+  assert (family == AF_INET || family == AF_INET6);
+
+  /* If our control connection is over IPv6, then we first try EPRT and then 
+   * LPRT if the former is not supported. If the control connection is over 
+   * IPv4, we simply issue the good old PORT request. */
+  if (family == AF_INET6)
+    {
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> EPRT ... ");
+      err = ftp_eprt (rbuf);
+
+      /* If EPRT is not supported try LPRT */
+      if (err == FTPPORTERR)
+        {
+          if (!opt.server_response)
+            logputs (LOG_VERBOSE, "==> LPRT ... ");
+          err = ftp_lprt (rbuf);
+        }
+    }
+  else 
+    {
+      if (!opt.server_response)
+        logputs (LOG_VERBOSE, "==> PORT ... ");
+      err = ftp_port (rbuf);
+    }
+
+  return err;
+}
+#else
+#define ftp_do_pasv ftp_pasv
+#define ftp_do_port ftp_port
+#endif
+
 /* Retrieves a file with denoted parameters through opening an FTP
    connection to the server.  It always closes the data connection,
    and closes the control connection in case of error.  */
@@ -541,7 +646,7 @@ Error in server response, closing control connection.\n"));
          unsigned short passive_port;
          if (!opt.server_response)
            logputs (LOG_VERBOSE, "==> PASV ... ");
-         err = ftp_pasv (&con->rbuf, &passive_addr, &passive_port);
+         err = ftp_do_pasv (&con->rbuf, &passive_addr, &passive_port);
          /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */
          switch (err)
            {
@@ -578,13 +683,16 @@ Error in server response, closing control connection.\n"));
            }   /* switch(err) */
          if (err==FTPOK)
            {
+             DEBUGP (("trying to connect to %s port %d\n", 
+                     pretty_print_address (&passive_addr),
+                     passive_port));
              dtsock = connect_to_one (&passive_addr, passive_port, 1);
              if (dtsock < 0)
                {
                  int save_errno = errno;
                  CLOSE (csock);
                  rbuf_uninitialize (&con->rbuf);
-                 logprintf (LOG_VERBOSE, _("couldn't connect to %s:%hu: %s\n"),
+                 logprintf (LOG_VERBOSE, _("couldn't connect to %s port %hu: %s\n"),
                             pretty_print_address (&passive_addr), passive_port,
                             strerror (save_errno));
                  return CONNECT_ERROR (save_errno);
@@ -600,7 +708,7 @@ Error in server response, closing control connection.\n"));
        {
          if (!opt.server_response)
            logputs (LOG_VERBOSE, "==> PORT ... ");
-         err = ftp_port (&con->rbuf);
+         err = ftp_do_port (&con->rbuf);
          /* FTPRERR, WRITEFAILED, bindport (CONSOCKERR, CONPORTERR, BINDERR,
             LISTENERR), HOSTERR, FTPPORTERR */
          switch (err)
@@ -738,6 +846,7 @@ Error in server response, closing control connection.\n"));
              logprintf (LOG_VERBOSE, "==> RETR %s ... ", u->file);
            }
        }
+
       err = ftp_retr (&con->rbuf, u->file);
       /* FTPRERR, WRITEFAILED, FTPNSFOD */
       switch (err)
@@ -929,6 +1038,7 @@ Error in server response, closing control connection.\n"));
     if (flush_res == EOF)
       res = -2;
   }
+
   /* If get_contents couldn't write to fp, bail out.  */
   if (res == -2)
     {
@@ -1196,7 +1306,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
            /* --dont-remove-listing was specified, so do count this towards the
               number of bytes and files downloaded. */
            {
-             downloaded_increase (len);
+             total_downloaded_bytes += len;
              opt.numurls++;
            }
 
@@ -1211,7 +1321,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
             downloaded if they're going to be deleted.  People seeding proxies,
             for instance, may want to know how many bytes and files they've
             downloaded through it. */
-         downloaded_increase (len);
+         total_downloaded_bytes += len;
          opt.numurls++;
 
          if (opt.delete_after)
@@ -1223,7 +1333,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con)
                logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
            }
        }
-      
+
       /* Restore the original leave-pendingness.  */
       if (orig_lp)
        con->cmd |= LEAVE_PENDING;
@@ -1337,7 +1447,7 @@ ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con)
     {
       char *old_target, *ofile;
 
-      if (downloaded_exceeds_quota ())
+      if (opt.quota && total_downloaded_bytes > opt.quota)
        {
          --depth;
          return QUOTEXC;
@@ -1541,7 +1651,7 @@ ftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con)
       int size;
       char *odir, *newdir;
 
-      if (downloaded_exceeds_quota ())
+      if (opt.quota && total_downloaded_bytes > opt.quota)
        break;
       if (f->type != FT_DIRECTORY)
        continue;
@@ -1587,12 +1697,24 @@ Not descending to `%s' as it is excluded/not-included.\n"), newdir);
       /* Set the time-stamp?  */
     }
 
-  if (opt.quota && opt.downloaded > opt.quota)
+  if (opt.quota && total_downloaded_bytes > opt.quota)
     return QUOTEXC;
   else
     return RETROK;
 }
 
+/* Return non-zero if S has a leading '/'  or contains '../' */
+static int
+has_insecure_name_p (const char *s)
+{
+  if (*s == '/')
+    return 1;
+
+  if (strstr(s, "../") != 0)
+    return 1;
+
+  return 0;
+}
 
 /* A near-top-level function to retrieve the files in a directory.
    The function calls ftp_get_listing, to get a linked list of files.
@@ -1605,20 +1727,19 @@ Not descending to `%s' as it is excluded/not-included.\n"), newdir);
 static uerr_t
 ftp_retrieve_glob (struct url *u, ccon *con, int action)
 {
-  struct fileinfo *f, *orig, *start;
+  struct fileinfo *f, *start;
   uerr_t res;
 
   con->cmd |= LEAVE_PENDING;
 
-  res = ftp_get_listing (u, con, &orig);
+  res = ftp_get_listing (u, con, &start);
   if (res != RETROK)
     return res;
-  start = orig;
   /* First: weed out that do not conform the global rules given in
      opt.accepts and opt.rejects.  */
   if (opt.accepts || opt.rejects)
     {
-      f = orig;
+      f = start;
       while (f)
        {
          if (f->type != FT_DIRECTORY && !acceptable (f->name))
@@ -1631,10 +1752,10 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
        }
     }
   /* Remove all files with possible harmful names */
-  f = orig;
+  f = start;
   while (f)
     {
-      if (has_insecure_name_p(f->name))
+      if (has_insecure_name_p (f->name))
        {
          logprintf (LOG_VERBOSE, _("Rejecting `%s'.\n"), f->name);
          f = delelement (f, &start);
@@ -1693,7 +1814,7 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action)
        }
     }
   freefileinfo (start);
-  if (downloaded_exceeds_quota ())
+  if (opt.quota && total_downloaded_bytes > opt.quota)
     return QUOTEXC;
   else
     /* #### Should we return `res' here?  */