]> sjero.net Git - wget/blobdiff - src/http.c
[svn] Allow Wget to be compiled with a K&R compiler.
[wget] / src / http.c
index ffb83d9a84983b97eaaf3047709aebdaad3e79b1..6dacacab060c68d1b03101e4e5c3dae5bd6a0e6b 100644 (file)
@@ -44,12 +44,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 # endif
 #endif
 
-#ifdef WINDOWS
-# include <winsock.h>
-#else
-# include <netdb.h>            /* for h_errno */
-#endif
-
 #include "wget.h"
 #include "utils.h"
 #include "url.h"
@@ -64,17 +58,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 # include "gen_sslfunc.h"
 #endif /* HAVE_SSL */
 #include "cookies.h"
+#ifdef USE_DIGEST
+# include "gen-md5.h"
+#endif
 
 extern char *version_string;
 
 #ifndef errno
 extern int errno;
 #endif
-#ifndef h_errno
-# ifndef __CYGWIN__
-extern int h_errno;
-# endif
-#endif
 \f
 static int cookies_loaded_p;
 
@@ -202,6 +194,10 @@ http_process_range (const char *hdr, void *arg)
   if (!strncasecmp (hdr, "bytes", 5))
     {
       hdr += 5;
+      /* "JavaWebServer/1.1.1" sends "bytes: x-y/z", contrary to the
+        HTTP spec. */
+      if (*hdr == ':')
+       ++hdr;
       hdr += skip_lws (hdr);
       if (!*hdr)
        return 0;
@@ -276,7 +272,7 @@ http_process_connection (const char *hdr, void *arg)
 /* Whether a persistent connection is active. */
 static int pc_active_p;
 /* Host and port of currently active persistent connection. */
-static unsigned char pc_last_host_ip[4];
+static struct address_list *pc_last_host_ip;
 static unsigned short pc_last_port;
 
 /* File descriptor of the currently active persistent connection. */
@@ -301,6 +297,11 @@ invalidate_persistent (void)
 #ifdef HAVE_SSL
   pc_active_ssl = 0;
 #endif /* HAVE_SSL */
+  if (pc_last_host_ip != NULL)
+    {
+      address_list_release (pc_last_host_ip);
+      pc_last_host_ip = NULL;
+    }
   DEBUGP (("Invalidating fd %d from further reuse.\n", pc_last_fd));
 }
 
@@ -312,15 +313,15 @@ invalidate_persistent (void)
 
    If a previous connection was persistent, it is closed. */
 
-static void
-register_persistent (const char *host, unsigned short port, int fd
 #ifdef HAVE_SSL
-                    , SSL *ssl
-#endif
-                    )
+static void
+register_persistent (const char *host, unsigned short port, int fd, SSL *ssl)
 {
-  int success;
-
+#else
+static void
+register_persistent (const char *host, unsigned short port, int fd)
+{
+#endif
   if (pc_active_p)
     {
       if (pc_last_fd == fd)
@@ -347,10 +348,13 @@ register_persistent (const char *host, unsigned short port, int fd
        }
     }
 
+  assert (pc_last_host_ip == NULL);
+
   /* This lookup_host cannot fail, because it has the results in the
      cache.  */
-  success = lookup_host (host, pc_last_host_ip);
-  assert (success);
+  pc_last_host_ip = lookup_host (host, 1);
+  assert (pc_last_host_ip != NULL);
+
   pc_last_port = port;
   pc_last_fd = fd;
   pc_active_p = 1;
@@ -361,17 +365,30 @@ register_persistent (const char *host, unsigned short port, int fd
   DEBUGP (("Registered fd %d for persistent reuse.\n", fd));
 }
 
+#ifdef HAVE_SSL
+# define SHUTDOWN_SSL(ssl) do {                \
+  if (ssl)                             \
+    shutdown_ssl (ssl);                        \
+} while (0)
+#else
+# define SHUTDOWN_SSL(ssl) 
+#endif
+
 /* Return non-zero if a persistent connection is available for
    connecting to HOST:PORT.  */
 
-static int
-persistent_available_p (const char *host, unsigned short port
 #ifdef HAVE_SSL
-                       , int ssl
-#endif
-                       )
+static int
+persistent_available_p (const char *host, unsigned short port, int ssl)
+{
+#else
+static int
+persistent_available_p (const char *host, unsigned short port)
 {
-  unsigned char this_host_ip[4];
+#endif
+  int success;
+  struct address_list *this_host_ip;
+
   /* First, check whether a persistent connection is active at all.  */
   if (!pc_active_p)
     return 0;
@@ -379,6 +396,7 @@ persistent_available_p (const char *host, unsigned short port
      (HOST, PORT) ordered pair.  */
   if (port != pc_last_port)
     return 0;
+
 #ifdef HAVE_SSL
   /* Second, a): check if current connection is (not) ssl, too.  This
      test is unlikely to fail because HTTP and HTTPS typicaly use
@@ -388,10 +406,19 @@ persistent_available_p (const char *host, unsigned short port
   if (ssl != pc_active_ssl)
     return 0;
 #endif /* HAVE_SSL */
-  if (!lookup_host (host, this_host_ip))
+
+  this_host_ip = lookup_host (host, 1);
+  if (!this_host_ip)
     return 0;
-  if (memcmp (pc_last_host_ip, this_host_ip, 4))
+
+  /* To equate the two host names for the purposes of persistent
+     connections, they need to share all the IP addresses in the
+     list.  */
+  success = address_list_match_all (pc_last_host_ip, this_host_ip);
+  address_list_release (this_host_ip);
+  if (!success)
     return 0;
+
   /* Third: check whether the connection is still open.  This is
      important because most server implement a liberal (short) timeout
      on persistent connections.  Wget can of course always reconnect
@@ -404,21 +431,16 @@ persistent_available_p (const char *host, unsigned short port
          let's invalidate the persistent connection before returning
          0.  */
       CLOSE (pc_last_fd);
+#ifdef HAVE_SSL
+      SHUTDOWN_SSL (pc_last_ssl);
+      pc_last_ssl = NULL;
+#endif
       invalidate_persistent ();
       return 0;
     }
   return 1;
 }
 
-#ifdef HAVE_SSL
-# define SHUTDOWN_SSL(ssl) do {                \
-  if (ssl)                             \
-    shutdown_ssl (ssl);                        \
-} while (0)
-#else
-# define SHUTDOWN_SSL(ssl) 
-#endif
-
 /* The idea behind these two CLOSE macros is to distinguish between
    two cases: one when the job we've been doing is finished, and we
    want to close the connection and leave, and two when something is
@@ -521,7 +543,6 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   int sock, hcount, num_written, all_length, statcode;
   long contlen, contrange;
   struct url *conn;
-  uerr_t err;
   FILE *fp;
   int auth_tried_already;
   struct rbuf rbuf;
@@ -529,7 +550,6 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   static SSL_CTX *ssl_ctx = NULL;
   SSL *ssl = NULL;
 #endif /* HAVE_SSL */
-  struct wget_timer *timer;
   char *cookies = NULL;
 
   /* Whether this connection will be kept alive after the HTTP request
@@ -543,11 +563,16 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   /* Whether keep-alive should be inhibited. */
   int inhibit_keep_alive;
 
+  /* Whether we need to print the host header with braces around host,
+     e.g. "Host: [3ffe:8100:200:2::2]:1234" instead of the usual
+     "Host: symbolic-name:1234". */
+  int squares_around_host = 0;
+
 #ifdef HAVE_SSL
   /* initialize ssl_ctx on first run */
   if (!ssl_ctx)
     {
-      err = init_ssl (&ssl_ctx);
+      uerr_t err = init_ssl (&ssl_ctx);
       if (err != 0)
        {
          switch (err)
@@ -622,42 +647,17 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
 #endif /* HAVE_SSL */
       )
     {
-      logprintf (LOG_VERBOSE, _("Connecting to %s:%hu... "),
-                conn->host, conn->port);
-      err = make_connection (&sock, conn->host, conn->port);
-      switch (err)
-       {
-       case HOSTERR:
-         logputs (LOG_VERBOSE, "\n");
-         logprintf (LOG_NOTQUIET, "%s: %s.\n", conn->host, herrmsg (h_errno));
-         return HOSTERR;
-         break;
-       case CONSOCKERR:
-         logputs (LOG_VERBOSE, "\n");
-         logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno));
-         return CONSOCKERR;
-         break;
-       case CONREFUSED:
-         logputs (LOG_VERBOSE, "\n");
-         logprintf (LOG_NOTQUIET,
-                    _("Connection to %s:%hu refused.\n"), conn->host,
-                    conn->port);
-         CLOSE (sock);
-         return CONREFUSED;
-       case CONERROR:
-         logputs (LOG_VERBOSE, "\n");
-         logprintf (LOG_NOTQUIET, "connect: %s\n", strerror (errno));
-         CLOSE (sock);
-         return CONERROR;
-         break;
-       case NOCONERROR:
-         /* Everything is fine!  */
-         logputs (LOG_VERBOSE, _("connected!\n"));
-         break;
-       default:
-         abort ();
-         break;
-       }
+      struct address_list *al = lookup_host (conn->host, 0);
+      if (!al)
+       return HOSTERR;
+      set_connection_host_name (conn->host);
+      sock = connect_to_many (al, conn->port, 0);
+      set_connection_host_name (NULL);
+      address_list_release (al);
+
+      if (sock < 0)
+       return errno == ECONNREFUSED ? CONREFUSED : CONERROR;
+
 #ifdef HAVE_SSL
      if (conn->scheme == SCHEME_HTTPS)
        if (connect_ssl (&ssl, ssl_ctx,sock) != 0)
@@ -754,8 +754,13 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
        }
       else
        {
+         /* Use the full path, i.e. one that includes the leading
+            slash and the query string, but is independent of proxy
+            setting.  */
+         char *pth = url_full_path (u);
          wwwauth = create_authorization_line (authenticate_h, user, passwd,
-                                              command, u->path);
+                                              command, pth);
+         xfree (pth);
        }
     }
 
@@ -810,8 +815,14 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   if (proxy)
     full_path = xstrdup (u->url);
   else
+    /* Use the full path, i.e. one that includes the leading slash and
+       the query string.  E.g. if u->path is "foo/bar" and u->query is
+       "param=value", full_path will be "/foo/bar?param=value".  */
     full_path = url_full_path (u);
 
+  if (strchr (u->host, ':'))
+    squares_around_host = 1;
+
   /* Allocate the memory for the request.  */
   request = (char *)alloca (strlen (command)
                            + strlen (full_path)
@@ -833,11 +844,12 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   sprintf (request, "\
 %s %s HTTP/1.0\r\n\
 User-Agent: %s\r\n\
-Host: %s%s\r\n\
+Host: %s%s%s%s\r\n\
 Accept: %s\r\n\
 %s%s%s%s%s%s%s%s\r\n",
           command, full_path,
-          useragent, u->host,
+          useragent,
+          squares_around_host ? "[" : "", u->host, squares_around_host ? "]" : "",
           port_maybe ? port_maybe : "",
           HTTP_ACCEPT,
           request_keep_alive ? request_keep_alive : "",
@@ -858,7 +870,7 @@ Accept: %s\r\n\
 
   /* Send the request to server.  */
 #ifdef HAVE_SSL
-  if (u->scheme == SCHEME_HTTPS)
+  if (conn->scheme == SCHEME_HTTPS)
     num_written = ssl_iwrite (ssl, request, strlen (request));
   else
 #endif /* HAVE_SSL */
@@ -881,7 +893,7 @@ Accept: %s\r\n\
   /* Before reading anything, initialize the rbuf.  */
   rbuf_initialize (&rbuf, sock);
 #ifdef HAVE_SSL
-  if (u->scheme == SCHEME_HTTPS)
+  if (conn->scheme == SCHEME_HTTPS)
     rbuf.ssl = ssl;
   else
     rbuf.ssl = NULL;
@@ -1216,6 +1228,8 @@ Accept: %s\r\n\
              /* In case the caller inspects. */
              hs->len = contlen;
              hs->res = 0;
+             /* Mark as successfully retrieved. */
+             *dt |= RETROKF;
              FREE_MAYBE (type);
              FREE_MAYBE (all_headers);
              CLOSE_INVALIDATE (sock);  /* would be CLOSE_FINISH, but there
@@ -1352,13 +1366,17 @@ Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
      should be some overhead information.  */
   if (opt.save_headers)
     fwrite (all_headers, 1, all_length, fp);
-  timer = wtimer_new ();
+
   /* Get the contents of the document.  */
   hs->res = get_contents (sock, fp, &hs->len, hs->restval,
                          (contlen != -1 ? contlen : 0),
-                         &rbuf, keep_alive);
-  hs->dltime = wtimer_elapsed (timer);
-  wtimer_delete (timer);
+                         &rbuf, keep_alive, &hs->dltime);
+
+  if (hs->res >= 0)
+    CLOSE_FINISH (sock);
+  else
+    CLOSE_INVALIDATE (sock);
+
   {
     /* Close or flush the file.  We have to be careful to check for
        error here.  Checking the result of fwrite() is not enough --
@@ -1372,7 +1390,6 @@ Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
       hs->res = -2;
   }
   FREE_MAYBE (all_headers);
-  CLOSE_FINISH (sock);
   if (hs->res == -2)
     return FWRITEERR;
   return RETRFINISHED;
@@ -2166,7 +2183,7 @@ dump_hash (unsigned char *buf, const unsigned char *hash)
 
 /* Take the line apart to find the challenge, and compose a digest
    authorization header.  See RFC2069 section 2.1.2.  */
-char *
+static char *
 digest_authentication_encode (const char *au, const char *user,
                              const char *passwd, const char *method,
                              const char *path)
@@ -2239,37 +2256,37 @@ digest_authentication_encode (const char *au, const char *user,
 
   /* Calculate the digest value.  */
   {
-    MD5_CONTEXT_TYPE ctx;
+    ALLOCA_MD5_CONTEXT (ctx);
     unsigned char hash[MD5_HASHLEN];
     unsigned char a1buf[MD5_HASHLEN * 2 + 1], a2buf[MD5_HASHLEN * 2 + 1];
     unsigned char response_digest[MD5_HASHLEN * 2 + 1];
 
     /* A1BUF = H(user ":" realm ":" password) */
-    MD5_INIT (&ctx);
-    MD5_UPDATE (user, strlen (user), &ctx);
-    MD5_UPDATE (":", 1, &ctx);
-    MD5_UPDATE (realm, strlen (realm), &ctx);
-    MD5_UPDATE (":", 1, &ctx);
-    MD5_UPDATE (passwd, strlen (passwd), &ctx);
-    MD5_FINISH (&ctx, hash);
+    gen_md5_init (ctx);
+    gen_md5_update ((unsigned char *)user, strlen (user), ctx);
+    gen_md5_update ((unsigned char *)":", 1, ctx);
+    gen_md5_update ((unsigned char *)realm, strlen (realm), ctx);
+    gen_md5_update ((unsigned char *)":", 1, ctx);
+    gen_md5_update ((unsigned char *)passwd, strlen (passwd), ctx);
+    gen_md5_finish (ctx, hash);
     dump_hash (a1buf, hash);
 
     /* A2BUF = H(method ":" path) */
-    MD5_INIT (&ctx);
-    MD5_UPDATE (method, strlen (method), &ctx);
-    MD5_UPDATE (":", 1, &ctx);
-    MD5_UPDATE (path, strlen (path), &ctx);
-    MD5_FINISH (&ctx, hash);
+    gen_md5_init (ctx);
+    gen_md5_update ((unsigned char *)method, strlen (method), ctx);
+    gen_md5_update ((unsigned char *)":", 1, ctx);
+    gen_md5_update ((unsigned char *)path, strlen (path), ctx);
+    gen_md5_finish (ctx, hash);
     dump_hash (a2buf, hash);
 
     /* RESPONSE_DIGEST = H(A1BUF ":" nonce ":" A2BUF) */
-    MD5_INIT (&ctx);
-    MD5_UPDATE (a1buf, MD5_HASHLEN * 2, &ctx);
-    MD5_UPDATE (":", 1, &ctx);
-    MD5_UPDATE (nonce, strlen (nonce), &ctx);
-    MD5_UPDATE (":", 1, &ctx);
-    MD5_UPDATE (a2buf, MD5_HASHLEN * 2, &ctx);
-    MD5_FINISH (&ctx, hash);
+    gen_md5_init (ctx);
+    gen_md5_update (a1buf, MD5_HASHLEN * 2, ctx);
+    gen_md5_update ((unsigned char *)":", 1, ctx);
+    gen_md5_update ((unsigned char *)nonce, strlen (nonce), ctx);
+    gen_md5_update ((unsigned char *)":", 1, ctx);
+    gen_md5_update (a2buf, MD5_HASHLEN * 2, ctx);
+    gen_md5_finish (ctx, hash);
     dump_hash (response_digest, hash);
 
     res = (char*) xmalloc (strlen (user)
@@ -2334,3 +2351,10 @@ create_authorization_line (const char *au, const char *user,
 #endif /* USE_DIGEST */
   return wwwauth;
 }
+\f
+void
+http_cleanup (void)
+{
+  if (pc_last_host_ip)
+    address_list_release (pc_last_host_ip);
+}