]> sjero.net Git - wget/blobdiff - src/retr.c
[svn] Limit the maximum amount of memory allocated by fd_read_hunk and its
[wget] / src / retr.c
index 3415ff02d5feb3c3accf2996fc64c25fd389cdef..978fb7284036960cc0dc3dc2605e8b7d90644d0f 100644 (file)
@@ -75,7 +75,7 @@ FILE *output_stream;
 int output_stream_regular;
 \f
 static struct {
-  long chunk_bytes;
+  wgint chunk_bytes;
   double chunk_start;
   double sleep_adjust;
 } limit_data;
@@ -92,7 +92,7 @@ limit_bandwidth_reset (void)
    is the timer that started at the beginning of download.  */
 
 static void
-limit_bandwidth (long bytes, struct wget_timer *timer)
+limit_bandwidth (wgint bytes, struct wget_timer *timer)
 {
   double delta_t = wtimer_read (timer) - limit_data.chunk_start;
   double expected;
@@ -110,12 +110,14 @@ limit_bandwidth (long bytes, struct wget_timer *timer)
       double t0, t1;
       if (slp < 200)
        {
-         DEBUGP (("deferring a %.2f ms sleep (%ld/%.2f).\n",
-                  slp, limit_data.chunk_bytes, delta_t));
+         DEBUGP (("deferring a %.2f ms sleep (%s/%.2f).\n",
+                  slp, number_to_static_string (limit_data.chunk_bytes),
+                  delta_t));
          return;
        }
-      DEBUGP (("\nsleeping %.2f ms for %ld bytes, adjust %.2f ms\n",
-              slp, limit_data.chunk_bytes, limit_data.sleep_adjust));
+      DEBUGP (("\nsleeping %.2f ms for %s bytes, adjust %.2f ms\n",
+              slp, number_to_static_string (limit_data.chunk_bytes),
+              limit_data.sleep_adjust));
 
       t0 = wtimer_read (timer);
       xsleep (slp / 1000);
@@ -142,8 +144,8 @@ limit_bandwidth (long bytes, struct wget_timer *timer)
    of data written.  */
 
 static int
-write_data (FILE *out, const char *buf, int bufsize, long *skip,
-           long *written)
+write_data (FILE *out, const char *buf, int bufsize, wgint *skip,
+           wgint *written)
 {
   if (!out)
     return 1;
@@ -192,8 +194,8 @@ write_data (FILE *out, const char *buf, int bufsize, long *skip,
    writing data, -2 is returned.  */
 
 int
-fd_read_body (int fd, FILE *out, long toread, long startpos,
-             long *qtyread, long *qtywritten, double *elapsed, int flags)
+fd_read_body (int fd, FILE *out, wgint toread, wgint startpos,
+             wgint *qtyread, wgint *qtywritten, double *elapsed, int flags)
 {
   int ret = 0;
 
@@ -213,11 +215,11 @@ fd_read_body (int fd, FILE *out, long toread, long startpos,
   int progress_interactive = 0;
 
   int exact = flags & rb_read_exactly;
-  long skip = 0;
+  wgint skip = 0;
 
   /* How much data we've read/written.  */
-  long sum_read = 0;
-  long sum_written = 0;
+  wgint sum_read = 0;
+  wgint sum_written = 0;
 
   if (flags & rb_skip_startpos)
     skip = startpos;
@@ -283,6 +285,13 @@ fd_read_body (int fd, FILE *out, long toread, long startpos,
        }
       ret = fd_read (fd, dlbuf, rdsize, tmout);
 
+      /* when retrieving from http-proxy wget sometimes does not trust the 
+       * file length reported by server.
+       * this check is to tell wget not to stubbornly try to read again and 
+       * again until another errno code was received. */
+      if ( ret == -1 && errno == ETIMEDOUT && sum_read == toread && toread > 0 )
+       break;
+
       if (ret == 0 || (ret < 0 && errno != ETIMEDOUT))
        break;                  /* read error */
       else if (ret < 0)
@@ -311,7 +320,7 @@ fd_read_body (int fd, FILE *out, long toread, long startpos,
       if (progress)
        progress_update (progress, ret, wtimer_read (timer));
 #ifdef WINDOWS
-      if (toread > 0)
+      if (toread > 0 && !opt.quiet)
        ws_percenttitle (100.0 *
                         (startpos + sum_read) / (startpos + toread));
 #endif
@@ -366,18 +375,23 @@ fd_read_body (int fd, FILE *out, long toread, long startpos,
    a read.  If the read returns a different amount of data, the
    process is retried until all data arrives safely.
 
-   BUFSIZE is the size of the initial buffer expected to read all the
-   data in the typical case.
+   SIZEHINT is the buffer size sufficient to hold all the data in the
+   typical case (it is used as the initial buffer size).  MAXSIZE is
+   the maximum amount of memory this function is allowed to allocate,
+   or 0 if no upper limit is to be enforced.
 
    This function should be used as a building block for other
    functions -- see fd_read_line as a simple example.  */
 
 char *
-fd_read_hunk (int fd, hunk_terminator_t hunk_terminator, int bufsize)
+fd_read_hunk (int fd, hunk_terminator_t terminator, long sizehint, long maxsize)
 {
+  long bufsize = sizehint;
   char *hunk = xmalloc (bufsize);
   int tail = 0;                        /* tail position in HUNK */
 
+  assert (maxsize >= bufsize);
+
   while (1)
     {
       const char *end;
@@ -391,7 +405,7 @@ fd_read_hunk (int fd, hunk_terminator_t hunk_terminator, int bufsize)
          xfree (hunk);
          return NULL;
        }
-      end = hunk_terminator (hunk, tail, pklen);
+      end = terminator (hunk, tail, pklen);
       if (end)
        {
          /* The data contains the terminator: we'll drain the data up
@@ -449,7 +463,17 @@ fd_read_hunk (int fd, hunk_terminator_t hunk_terminator, int bufsize)
 
       if (tail == bufsize - 1)
        {
+         /* Double the buffer size, but refuse to allocate more than
+            MAXSIZE bytes.  */
+         if (maxsize && bufsize >= maxsize)
+           {
+             xfree (hunk);
+             errno = ENOMEM;
+             return NULL;
+           }
          bufsize <<= 1;
+         if (maxsize && bufsize > maxsize)
+           bufsize = maxsize;
          hunk = xrealloc (hunk, bufsize);
        }
     }
@@ -465,8 +489,14 @@ line_terminator (const char *hunk, int oldlen, int peeklen)
   return NULL;
 }
 
+/* The maximum size of the single line we agree to accept.  This is
+   not meant to impose an arbitrary limit, but to protect the user
+   from Wget slurping up available memory upon encountering malicious
+   or buggy server output.  Define it to 0 to remove the limit.  */
+#define FD_READ_LINE_MAX 4096
+
 /* Read one line from FD and return it.  The line is allocated using
-   malloc.
+   malloc, but is never larger than FD_READ_LINE_MAX.
 
    If an error occurs, or if no data can be read, NULL is returned.
    In the former case errno indicates the error condition, and in the
@@ -475,17 +505,17 @@ line_terminator (const char *hunk, int oldlen, int peeklen)
 char *
 fd_read_line (int fd)
 {
-  return fd_read_hunk (fd, line_terminator, 128);
+  return fd_read_hunk (fd, line_terminator, 128, FD_READ_LINE_MAX);
 }
 \f
 /* Return a printed representation of the download rate, as
    appropriate for the speed.  If PAD is non-zero, strings will be
    padded to the width of 7 characters (xxxx.xx).  */
 char *
-retr_rate (long bytes, double msecs, int pad)
+retr_rate (wgint bytes, double msecs, int pad)
 {
   static char res[20];
-  static char *rate_names[] = {"B/s", "KB/s", "MB/s", "GB/s" };
+  static const char *rate_names[] = {"B/s", "KB/s", "MB/s", "GB/s" };
   int units = 0;
 
   double dlrate = calc_rate (bytes, msecs, &units);
@@ -502,7 +532,7 @@ retr_rate (long bytes, double msecs, int pad)
    UNITS is zero for B/s, one for KB/s, two for MB/s, and three for
    GB/s.  */
 double
-calc_rate (long bytes, double msecs, int *units)
+calc_rate (wgint bytes, double msecs, int *units)
 {
   double dlrate;
 
@@ -690,7 +720,7 @@ retrieve_url (const char *origurl, char **file, char **newloc,
       newloc_parsed = url_parse (mynewloc, &up_error_code);
       if (!newloc_parsed)
        {
-         logprintf (LOG_NOTQUIET, "%s: %s.\n", mynewloc,
+         logprintf (LOG_NOTQUIET, "%s: %s.\n", escnonprint_uri (mynewloc),
                     url_error (up_error_code));
          url_free (u);
          xfree (url);
@@ -905,7 +935,7 @@ rotate_backups(const char *fname)
   int maxlen = strlen (fname) + 1 + numdigit (opt.backups) + 1;
   char *from = (char *)alloca (maxlen);
   char *to = (char *)alloca (maxlen);
-  struct stat sb;
+  struct_stat sb;
   int i;
 
   if (stat (fname, &sb) == 0)