gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libmicrohttpd] branch master updated (9a18a4f8 -> c63be03c)


From: gnunet
Subject: [libmicrohttpd] branch master updated (9a18a4f8 -> c63be03c)
Date: Mon, 15 Aug 2022 20:38:26 +0200

This is an automated email from the git hooks/post-receive script.

karlson2k pushed a change to branch master
in repository libmicrohttpd.

    from 9a18a4f8 test_auth_parse: added check of two auths types parsing in 
one request
     new 5338e4cb digestauth: use internal header for mutex instead of system 
header
     new c6b2aee8 microhttpd: minor doxy improvement
     new 067b6234 daemon.c: cosmetics
     new 0d856953 digestauth: add log warning for empty random data
     new 0b9d022e digestauth: corrected log message for RFC2069 mode
     new 39b9f6cf digestauth: warn about RFC2069 used with SHA-256
     new 477f6149 digestauth: do not use "algorithm" in response header in 
RFC2069 mode
     new 7d2bf243 digestauth: added log messages if realm is rejected due to 
its size
     new b115e254 mhd_str: added function for bin to hex without 
zero-termination
     new 25863e1c daemon.c: fixed and simplified sockaddr alignment handling
     new 2f8eefad Added MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE to control how 
to generate and check nonces for Digest Auth
     new 63c6f68b test_digestauth2: added testing with two requests
     new e4092f1d test_digestauth2: added testing of nonce bind options
     new 8a210d28 test_digestauth2: reuse the same connection for the second 
request
     new da62262d test_digestauth2: added third request with new connection
     new e16789ef test_digestauth2: minor improvements
     new 451b0d9f digestauth: minor comment correction
     new e1d6b763 test_digestauth_concurrent: added debug print
     new 21d8f546 digestauth: updated the method of nonce generation in default 
mode
     new c63be03c calculate_nonce(): added comments, minor code corrections

The 20 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/include/microhttpd.h                  |  98 +++++++-
 src/microhttpd/daemon.c                   |  75 +++---
 src/microhttpd/digestauth.c               | 351 +++++++++++++++++++---------
 src/microhttpd/internal.h                 |   6 +-
 src/microhttpd/mhd_str.c                  |  15 +-
 src/microhttpd/mhd_str.h                  |  21 +-
 src/microhttpd/test_str_base64.c          |   6 +-
 src/microhttpd/test_str_bin_hex.c         |   2 +-
 src/testcurl/.gitignore                   |   4 +
 src/testcurl/Makefile.am                  |  18 +-
 src/testcurl/test_digestauth2.c           | 365 +++++++++++++++++++++++++-----
 src/testcurl/test_digestauth_concurrent.c |  15 +-
 12 files changed, 764 insertions(+), 212 deletions(-)

diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 0b47f095..d3765b6c 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -96,7 +96,7 @@ extern "C"
  * they are parsed as decimal numbers.
  * Example: 0x01093001 = 1.9.30-1.
  */
-#define MHD_VERSION 0x00097530
+#define MHD_VERSION 0x00097531
 
 /* If generic headers don't work on your platform, include headers
    which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t',
@@ -1540,6 +1540,69 @@ typedef int
                                     void **psk,
                                     size_t *psk_size);
 
+/**
+ * Values for #MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE.
+ *
+ * These values can limit the scope of validity of MHD-generated nonces.
+ * Values can be combined with bitwise OR.
+ * Any value, except #MHD_DAUTH_BIND_NONCE_NONE, enforce function
+ * #MHD_digest_auth_check3() (and similar) to check nonce by re-generating
+ * it again with the same parameters, which is CPU-intensive operation.
+ * @note Available since #MHD_VERSION 0x00097531
+ */
+enum MHD_DAuthBindNonce
+{
+  /**
+   * Generated nonces are valid for any request from any client until expired.
+   * This is default and recommended value.
+   * #MHD_digest_auth_check3() (and similar function) would check only whether
+   * the nonce value that is used by client has been generated by MHD and not
+   * expired yet.
+   * It is recommended because RFC 7616 allows clients to use the same nonce
+   * for any request in the same "protection space".
+   * CPU is loaded less when this value is used when checking client's
+   * authorisation request.
+   * This mode gives MHD maximum flexibility for nonces generation and can
+   * prevent possible nonce collisions (and corresponding log warning messages)
+   * when clients' requests are intensive.
+   * This value cannot be combined with other values.
+   */
+  MHD_DAUTH_BIND_NONCE_NONE = 0,
+
+  /**
+   * Generated nonces are valid only for the same realm.
+   */
+  MHD_DAUTH_BIND_NONCE_REALM = 1 << 0,
+
+  /**
+   * Generated nonces are valid only for the same URI (excluding parameters
+   * after '?' in URI) and request method (GET, POST etc).
+   * Not recommended unless "protection space" is limited to a single URI as
+   * RFC 7616 allows clients to re-use server-generated nonces for any URI
+   * in the same "protection space" which is by default consists of all server
+   * URIs.
+   * This was default (and only supported) value before #MHD_VERSION 0x00097518
+   */
+  MHD_DAUTH_BIND_NONCE_URI = 1 << 1,
+
+  /**
+   * Generated nonces are valid only for the same URI including URI parameters
+   * and request method (GET, POST etc).
+   * This value implies #MHD_DAUTH_BIND_NONCE_URI.
+   * Not recommended for that same reasons as #MHD_DAUTH_BIND_NONCE_URI.
+   */
+  MHD_DAUTH_BIND_NONCE_URI_PARAMS = 1 << 2,
+
+  /**
+   * Generated nonces are valid only for the single client's IP.
+   * While it looks like security improvement, in practice the same client may
+   * jump from one IP to another (mobile or Wi-Fi handover, DHCP re-assignment,
+   * Multi-NAT, different proxy chain and other reasons), while IP address
+   * spoofing could be used relatively easily.
+   */
+  MHD_DAUTH_BIND_NONCE_CLIENT_IP = 1 << 3
+} _MHD_FLAGS_ENUM;
+
 /**
  * @brief MHD options.
  *
@@ -1943,7 +2006,18 @@ enum MHD_OPTION
    * @sa #MHD_OPTION_DIGEST_AUTH_RANDOM
    * @note Available since #MHD_VERSION 0x00097529
    */
-  MHD_OPTION_DIGEST_AUTH_RANDOM_COPY = 35
+  MHD_OPTION_DIGEST_AUTH_RANDOM_COPY = 35,
+
+  /**
+   * Allow to controls the scope of validity of MHD-generated nonces.
+   * This regulates how "nonces" are generated and how "nonces" are checked by
+   * #MHD_digest_auth_check3() and similar functions.
+   * This option should be followed by an 'unsigned int` argument with value
+   * formed as bitwise OR combination of #MHD_DAuthBindNonce values.
+   * When not specified, default value #MHD_DAUTH_BIND_NONCE_NONE is used.
+   * @note Available since #MHD_VERSION 0x00097531
+   */
+  MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE = 36
 } _MHD_FIXED_ENUM;
 
 
@@ -4906,7 +4980,7 @@ MHD_digest_auth_get_username3 (struct MHD_Connection 
*connection);
  *
  * All error values are zero or negative.
  *
- * @note Available since #MHD_VERSION 0x00097521
+ * @note Available since #MHD_VERSION 0x00097531
  */
 enum MHD_DigestAuthResult
 {
@@ -4967,6 +5041,20 @@ enum MHD_DigestAuthResult
    */
   MHD_DAUTH_NONCE_STALE = -17,
 
+  /**
+   * The 'nonce' was generated by MHD for other conditions.
+   * This value is only returned if #MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE
+   * is set to anything other than #MHD_DAUTH_BIND_NONCE_NONE.
+   * The interpretation of this code could be different. For example, if
+   * #MHD_DAUTH_BIND_NONCE_URI is set and client just used the same 'nonce' for
+   * another URI, the code could be handled as #MHD_DAUTH_NONCE_STALE as
+   * it is allowed to re-use nonces for other URIs in the same "protection
+   * space". However, if only #MHD_DAUTH_BIND_NONCE_CLIENT_IP bit is set and
+   * it is know that clients have fixed IP addresses, this return code could
+   * be handled like #MHD_DAUTH_NONCE_WRONG.
+   */
+  MHD_DAUTH_NONCE_OTHER_COND = -18,
+
   /**
    * The 'nonce' is wrong. May indicate an attack attempt.
    */
@@ -5092,8 +5180,8 @@ MHD_digest_auth_check_digest3 (struct MHD_Connection 
*connection,
  *               same authorisation could be used, URIs can be in form
  *               "path-absolute" (the path for the same host with initial 
slash)
  *               or in form "absolute-URI" (the full path with protocol), in
- *               any case client may assume that any URI which starts with
- *               any of specified URI is in the same "protection space";
+ *               any case client may assume that URI is in the same "protection
+ *               space" if it starts with any of values specified here;
  *               could be NULL (clients typically assume that the same
  *               credentials could be used for any URI on the same host)
  * @param response the reply to send; should contain the "access denied"
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c
index ecce17b5..6a92e733 100644
--- a/src/microhttpd/daemon.c
+++ b/src/microhttpd/daemon.c
@@ -321,7 +321,7 @@ MHD_ip_addr_compare (const void *a1,
  * @return #MHD_YES on success and #MHD_NO otherwise (e.g., invalid address 
type)
  */
 static enum MHD_Result
-MHD_ip_addr_to_key (const struct sockaddr *addr,
+MHD_ip_addr_to_key (const struct sockaddr_storage *addr,
                     socklen_t addrlen,
                     struct MHD_IPCount *key)
 {
@@ -332,13 +332,12 @@ MHD_ip_addr_to_key (const struct sockaddr *addr,
   /* IPv4 addresses */
   if (sizeof (struct sockaddr_in) <= (size_t) addrlen)
   {
-    if (AF_INET == addr->sa_family)
+    if (AF_INET == addr->ss_family)
     {
       key->family = AF_INET;
       memcpy (&key->addr.ipv4,
-              ((const uint8_t *) addr)
-              + _MHD_OFFSETOF (struct sockaddr_in, sin_addr),
-              sizeof(((struct sockaddr_in *) NULL)->sin_addr));
+              &((const struct sockaddr_in *) addr)->sin_addr,
+              sizeof(((const struct sockaddr_in *) NULL)->sin_addr));
       return MHD_YES;
     }
   }
@@ -347,13 +346,12 @@ MHD_ip_addr_to_key (const struct sockaddr *addr,
   if (sizeof (struct sockaddr_in6) <= (size_t) addrlen)
   {
     /* IPv6 addresses */
-    if (AF_INET6 == addr->sa_family)
+    if (AF_INET6 == addr->ss_family)
     {
       key->family = AF_INET6;
       memcpy (&key->addr.ipv6,
-              ((const uint8_t *) addr)
-              + _MHD_OFFSETOF (struct sockaddr_in6, sin6_addr),
-              sizeof(((struct sockaddr_in6 *) NULL)->sin6_addr));
+              &((const struct sockaddr_in6 *) addr)->sin6_addr,
+              sizeof(((const struct sockaddr_in6 *) NULL)->sin6_addr));
       return MHD_YES;
     }
   }
@@ -377,7 +375,7 @@ MHD_ip_addr_to_key (const struct sockaddr *addr,
  */
 static enum MHD_Result
 MHD_ip_limit_add (struct MHD_Daemon *daemon,
-                  const struct sockaddr *addr,
+                  const struct sockaddr_storage *addr,
                   socklen_t addrlen)
 {
   struct MHD_IPCount *newkeyp;
@@ -445,7 +443,7 @@ MHD_ip_limit_add (struct MHD_Daemon *daemon,
  */
 static void
 MHD_ip_limit_del (struct MHD_Daemon *daemon,
-                  const struct sockaddr *addr,
+                  const struct sockaddr_storage *addr,
                   socklen_t addrlen)
 {
   struct MHD_IPCount search_key;
@@ -2423,7 +2421,7 @@ psk_gnutls_adapter (gnutls_session_t session,
 static struct MHD_Connection *
 new_connection_prepare_ (struct MHD_Daemon *daemon,
                          MHD_socket client_socket,
-                         const struct sockaddr *addr,
+                         const struct sockaddr_storage *addr,
                          socklen_t addrlen,
                          bool external_add,
                          bool non_blck,
@@ -2461,7 +2459,7 @@ new_connection_prepare_ (struct MHD_Daemon *daemon,
   /* apply connection acceptance policy if present */
   if ( (NULL != daemon->apc) &&
        (MHD_NO == daemon->apc (daemon->apc_cls,
-                               addr,
+                               (const struct sockaddr *) addr,
                                addrlen)) )
   {
 #if _MHD_DEBUG_CLOSE
@@ -2964,7 +2962,7 @@ new_connection_process_ (struct MHD_Daemon *daemon,
 static enum MHD_Result
 internal_add_connection (struct MHD_Daemon *daemon,
                          MHD_socket client_socket,
-                         const struct sockaddr *addr,
+                         const struct sockaddr_storage *addr,
                          socklen_t addrlen,
                          bool external_add,
                          bool non_blck,
@@ -3491,6 +3489,7 @@ MHD_add_connection (struct MHD_Daemon *daemon,
 {
   bool sk_nonbl;
   bool sk_spipe_supprs;
+  struct sockaddr_storage addrstorage;
 
   /* NOT thread safe with internal thread. TODO: fix thread safety. */
   if ((0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) &&
@@ -3591,6 +3590,13 @@ MHD_add_connection (struct MHD_Daemon *daemon,
 #endif
   }
 
+  /* Copy to sockaddr_storage structure to avoid alignment problems */
+  if (0 < addrlen)
+    memcpy (&addrstorage, addr, (size_t) addrlen);
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
+  addrstorage.ss_len = addrlen; /* Force set the right length */
+#endif /* HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */
+
 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
   if (NULL != daemon->worker_pool)
   {
@@ -3606,7 +3612,7 @@ MHD_add_connection (struct MHD_Daemon *daemon,
       if (worker->connections < worker->connection_limit)
         return internal_add_connection (worker,
                                         client_socket,
-                                        addr,
+                                        &addrstorage,
                                         addrlen,
                                         true,
                                         sk_nonbl,
@@ -3624,7 +3630,7 @@ MHD_add_connection (struct MHD_Daemon *daemon,
 
   return internal_add_connection (daemon,
                                   client_socket,
-                                  addr,
+                                  &addrstorage,
                                   addrlen,
                                   true,
                                   sk_nonbl,
@@ -3650,12 +3656,7 @@ MHD_add_connection (struct MHD_Daemon *daemon,
 static enum MHD_Result
 MHD_accept_connection (struct MHD_Daemon *daemon)
 {
-#ifdef HAVE_INET6
-  struct sockaddr_in6 addrstorage;
-#else
-  struct sockaddr_in addrstorage;
-#endif
-  struct sockaddr *addr = (struct sockaddr *) &addrstorage;
+  struct sockaddr_storage addrstorage;
   socklen_t addrlen;
   MHD_socket s;
   MHD_socket fd;
@@ -3669,16 +3670,21 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
   mhd_assert (NULL == daemon->worker_pool);
 #endif /* MHD_USE_THREADS */
 
-  addrlen = sizeof (addrstorage);
-  memset (addr,
-          0,
-          sizeof (addrstorage));
   if ( (MHD_INVALID_SOCKET == (fd = daemon->listen_fd)) ||
        (daemon->was_quiesced) )
     return MHD_NO;
+
+  addrlen = (socklen_t) sizeof (addrstorage);
+  memset (&addrstorage,
+          0,
+          (size_t) addrlen);
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
+  addrstorage.ss_len = addrlen;
+#endif /* HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN */
+
 #ifdef USE_ACCEPT4
   s = accept4 (fd,
-               addr,
+               &addrstorage,
                &addrlen,
                SOCK_CLOEXEC_OR_ZERO | SOCK_NONBLOCK_OR_ZERO
                | SOCK_NOSIGPIPE_OR_ZERO);
@@ -3691,7 +3697,7 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
   sk_cloexec = (SOCK_CLOEXEC_OR_ZERO != 0);
 #else  /* ! USE_ACCEPT4 */
   s = accept (fd,
-              addr,
+              (struct sockaddr *) &addrstorage,
               &addrlen);
 #ifdef MHD_ACCEPT_INHERIT_NONBLOCK
   sk_nonbl = daemon->listen_nonblk;
@@ -3821,7 +3827,7 @@ MHD_accept_connection (struct MHD_Daemon *daemon)
 #endif
   (void) internal_add_connection (daemon,
                                   s,
-                                  addr,
+                                  &addrstorage,
                                   addrlen,
                                   false,
                                   sk_nonbl,
@@ -6020,8 +6026,8 @@ parse_options_va (struct MHD_Daemon *daemon,
       /* Next comparison could be always false on some platforms and whole 
branch will
        * be optimized out on these platforms. On others it will be compiled 
into real
        * check. */
-      else if (daemon->worker_pool_size >= (SIZE_MAX / sizeof (struct
-                                                               MHD_Daemon)))   
         /* Compiler may warn on some platforms, ignore warning. */
+      else if (daemon->worker_pool_size >=
+               (SIZE_MAX / sizeof (struct MHD_Daemon)))            /* Compiler 
may warn on some platforms, ignore warning. */
       {
 #ifdef HAVE_MESSAGES
         MHD_DLOG (daemon,
@@ -6251,6 +6257,12 @@ parse_options_va (struct MHD_Daemon *daemon,
       daemon->nonce_nc_size = va_arg (ap,
                                       unsigned int);
       break;
+    case MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE:
+      daemon->dauth_bind_type = va_arg (ap,
+                                        unsigned int);
+      if (0 != (daemon->dauth_bind_type & MHD_DAUTH_BIND_NONCE_URI_PARAMS))
+        daemon->dauth_bind_type |= MHD_DAUTH_BIND_NONCE_URI;
+      break;
 #endif
     case MHD_OPTION_LISTEN_SOCKET:
       if (0 != (daemon->options & MHD_USE_NO_LISTEN_SOCKET))
@@ -6373,6 +6385,7 @@ parse_options_va (struct MHD_Daemon *daemon,
         case MHD_OPTION_LISTENING_ADDRESS_REUSE:
         case MHD_OPTION_LISTEN_BACKLOG_SIZE:
         case MHD_OPTION_SERVER_INSANITY:
+        case MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE:
           if (MHD_NO == parse_options (daemon,
                                        servaddr,
                                        opt,
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index e9a7e35f..1061512d 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -23,7 +23,9 @@
  * @author Amr Ali
  * @author Matthieu Speder
  * @author Christian Grothoff (RFC 7616 support)
- * @author Karlson2k (Evgeny Grin)
+ * @author Karlson2k (Evgeny Grin) (fixes, new API, improvements, large 
rewrite,
+ *                                  many RFC 7616 features implementation,
+ *                                  old RFC 2069 support)
  */
 #include "digestauth.h"
 #include "gen_auth.h"
@@ -33,19 +35,13 @@
 #include "response.h"
 #include "md5.h"
 #include "sha256.h"
+#include "mhd_locks.h"
 #include "mhd_mono_clock.h"
 #include "mhd_str.h"
 #include "mhd_compat.h"
 #include "mhd_bithelpers.h"
 #include "mhd_assert.h"
 
-#if defined(MHD_W32_MUTEX_)
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN 1
-#endif /* !WIN32_LEAN_AND_MEAN */
-#include <windows.h>
-#endif /* MHD_W32_MUTEX_ */
-
 
 /**
  * Allow re-use of the nonce-nc map array slot after #REUSE_TIMEOUT seconds,
@@ -1317,88 +1313,164 @@ MHD_digest_auth_get_username (struct MHD_Connection 
*connection)
 /**
  * Calculate the server nonce so that it mitigates replay attacks
  * The current format of the nonce is ...
- * H(timestamp ":" method ":" random ":" uri ":" realm) + Hex(timestamp)
+ * H(various parameters) + Hex(timestamp)
  *
  * @param nonce_time The amount of time in seconds for a nonce to be invalid
- * @param method HTTP method
- * @param rnd A pointer to a character array for the random seed
+ * @param mthd_e HTTP method as enum value
+ * @param method HTTP method as a string
+ * @param rnd the pointer to a character array for the random seed
  * @param rnd_size The size of the random seed array @a rnd
- * @param uri HTTP URI (in MHD, without the arguments ("?k=v")
+ * @param saddr the pointer to the socket address structure
+ * @param saddr_size the size of the socket address structure @a saddr
+ * @param uri the HTTP URI (in MHD, without the arguments ("?k=v")
+ * @param uri_len the length of the @a uri
+ * @param first_header the pointer to the first request's header
  * @param realm A string of characters that describes the realm of auth.
  * @param realm_len the length of the @a realm.
+ * @param bind_options the nonce bind options (#MHD_DAuthBindNonce values).
  * @param da digest algorithm to use
- * @param[out] nonce A pointer to a character array for the nonce to put in,
- *        must provide NONCE_STD_LEN(digest_get_size(da))+1 bytes
+ * @param[out] nonce the pointer to a character array for the nonce to put in,
+ *                   must provide NONCE_STD_LEN(digest_get_size(da)) bytes,
+ *                   result is NOT zero-terminated
  */
 static void
 calculate_nonce (uint64_t nonce_time,
+                 enum MHD_HTTP_Method mthd_e,
                  const char *method,
                  const char *rnd,
                  size_t rnd_size,
+                 const struct sockaddr_storage *saddr,
+                 size_t saddr_size,
                  const char *uri,
                  size_t uri_len,
-                 struct MHD_HTTP_Req_Header *first_header,
+                 const struct MHD_HTTP_Req_Header *first_header,
                  const char *realm,
                  size_t realm_len,
+                 unsigned int bind_options,
                  struct DigestAlgorithm *da,
                  char *nonce)
 {
-  uint8_t timestamp[TIMESTAMP_BIN_SIZE];
-  struct MHD_HTTP_Req_Header *h;
-
   digest_init (da);
-  /* If the nonce_time is milliseconds, then the same 48 bit value will repeat
-   * every 8 925 years, which is more than enough to mitigate a replay attack 
*/
+  if (1)
+  {
+    /* Add the timestamp to the hash calculation */
+    uint8_t timestamp[TIMESTAMP_BIN_SIZE];
+    /* If the nonce_time is milliseconds, then the same 48 bit value will 
repeat
+     * every 8 919 years, which is more than enough to mitigate a replay 
attack */
 #if TIMESTAMP_BIN_SIZE != 6
 #error The code needs to be updated here
 #endif
-  timestamp[0] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 0)));
-  timestamp[1] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 1)));
-  timestamp[2] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 2)));
-  timestamp[3] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 3)));
-  timestamp[4] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 4)));
-  timestamp[5] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 5)));
-  digest_update (da,
-                 timestamp,
-                 sizeof (timestamp));
-  digest_update_with_colon (da);
-  digest_update_str (da, method);
-  digest_update_with_colon (da);
+    timestamp[0] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 
0)));
+    timestamp[1] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 
1)));
+    timestamp[2] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 
2)));
+    timestamp[3] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 
3)));
+    timestamp[4] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 
4)));
+    timestamp[5] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 
5)));
+    MHD_bin_to_hex (timestamp,
+                    sizeof (timestamp),
+                    nonce + digest_get_size (da) * 2);
+    digest_update (da,
+                   timestamp,
+                   sizeof (timestamp));
+  }
   if (rnd_size > 0)
+  {
+    /* Add the unique random value to the hash calculation */
+    digest_update_with_colon (da);
     digest_update (da,
                    rnd,
                    rnd_size);
-  digest_update_with_colon (da);
-  digest_update (da,
-                 uri,
-                 uri_len);
-  for (h = first_header; NULL != h; h = h->next)
+  }
+  if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) &&
+       (0 != saddr_size) )
   {
-    if (MHD_GET_ARGUMENT_KIND != h->kind)
-      continue;
-    digest_update (da, "\0", 2);
-    if (0 != h->header_size)
-      digest_update (da, h->header, h->header_size);
-    digest_update (da, "", 1);
-    if (0 != h->value_size)
-      digest_update (da, h->value, h->value_size);
+    /* Add full client address including source port to make unique nonces
+     * for requests received exactly at the same time */
+    digest_update_with_colon (da);
+    digest_update (da,
+                   saddr,
+                   saddr_size);
+  }
+  if ( (0 != (bind_options & MHD_DAUTH_BIND_NONCE_CLIENT_IP)) &&
+       (0 != saddr_size) )
+  {
+    /* Add the client's IP address to the hash calculation */
+    digest_update_with_colon (da);
+    if (AF_INET == saddr->ss_family)
+      digest_update (da,
+                     &((const struct sockaddr_in *) saddr)->sin_addr,
+                     sizeof(((const struct sockaddr_in *) saddr)->sin_addr));
+#ifdef HAVE_INET6
+    else if (AF_INET6 == saddr->ss_family)
+      digest_update (da,
+                     &((const struct sockaddr_in6 *) saddr)->sin6_addr,
+                     sizeof(((const struct sockaddr_in6 *) saddr)->sin6_addr));
+#endif /* HAVE_INET6 */
+  }
+  if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) ||
+       (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI)))
+  {
+    /* Add the request method to the hash calculation */
+    digest_update_with_colon (da);
+    if (MHD_HTTP_MTHD_OTHER != mthd_e)
+    {
+      uint8_t mthd_for_hash;
+      if (MHD_HTTP_MTHD_HEAD != mthd_e)
+        mthd_for_hash = (uint8_t) mthd_e;
+      else /* Treat HEAD method in the same way as GET method */
+        mthd_for_hash = (uint8_t) MHD_HTTP_MTHD_GET;
+      digest_update (da,
+                     &mthd_for_hash,
+                     sizeof(mthd_for_hash));
+    }
+    else
+      digest_update_str (da, method);
+  }
+
+  if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI))
+  {
+    /* Add the request URI to the hash calculation */
+    digest_update_with_colon (da);
+
+    digest_update (da,
+                   uri,
+                   uri_len);
+  }
+  if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI_PARAMS))
+  {
+    /* Add the request URI parameters to the hash calculation */
+    const struct MHD_HTTP_Req_Header *h;
+
+    digest_update_with_colon (da);
+    for (h = first_header; NULL != h; h = h->next)
+    {
+      if (MHD_GET_ARGUMENT_KIND != h->kind)
+        continue;
+      digest_update (da, "\0", 2);
+      if (0 != h->header_size)
+        digest_update (da, h->header, h->header_size);
+      digest_update (da, "", 1);
+      if (0 != h->value_size)
+        digest_update (da, h->value, h->value_size);
+    }
+  }
+  if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) ||
+       (0 != (bind_options & MHD_DAUTH_BIND_NONCE_REALM)))
+  {
+    /* Add the realm to the hash calculation */
+    digest_update_with_colon (da);
+    digest_update (da,
+                   realm,
+                   realm_len);
   }
-  digest_update_with_colon (da);
-  digest_update (da,
-                 realm,
-                 realm_len);
   if (1)
   {
-    const unsigned int digest_size = digest_get_size (da);
     uint8_t hash[MAX_DIGEST];
     digest_calc_hash (da, hash);
     MHD_bin_to_hex (hash,
-                    digest_size,
+                    digest_get_size (da),
                     nonce);
   }
-  MHD_bin_to_hex (timestamp,
-                  sizeof (timestamp),
-                  nonce + digest_get_size (da) * 2);
 }
 
 
@@ -1470,7 +1542,8 @@ is_slot_available (const struct MHD_NonceNc *const nn,
  * @param realm_len the length of the @a realm
  * @param da the digest algorithm to use
  * @param[out] nonce the pointer to a character array for the nonce to put in,
- *        must provide NONCE_STD_LEN(digest_get_size(da))+1 bytes
+ *                   must provide NONCE_STD_LEN(digest_get_size(da)) bytes,
+ *                   result is NOT zero-terminated
  * @return true if the new nonce has been added to the nonce-nc map array,
  *         false otherwise.
  */
@@ -1491,14 +1564,18 @@ calculate_add_nonce (struct MHD_Connection *const 
connection,
   mhd_assert (0 != nonce_size);
 
   calculate_nonce (timestamp,
+                   connection->rq.http_mthd,
                    connection->rq.method,
                    daemon->digest_auth_random,
                    daemon->digest_auth_rand_size,
+                   connection->addr,
+                   (size_t) connection->addr_len,
                    connection->rq.url,
                    connection->rq.url_len,
                    connection->rq.headers_received,
                    realm,
                    realm_len,
+                   daemon->dauth_bind_type,
                    da,
                    nonce);
 
@@ -1538,8 +1615,9 @@ calculate_add_nonce (struct MHD_Connection *const 
connection,
  * @param connection the MHD connection structure
  * @param realm A string of characters that describes the realm of auth.
  * @param da digest algorithm to use
- * @param[out] nonce A pointer to a character array for the nonce to put in,
- *        must provide NONCE_STD_LEN(digest_get_size(da))+1 bytes
+ * @param[out] nonce the pointer to a character array for the nonce to put in,
+ *                   must provide NONCE_STD_LEN(digest_get_size(da)) bytes,
+ *                   result is NOT zero-terminated
  */
 static bool
 calculate_add_nonce_with_retry (struct MHD_Connection *const connection,
@@ -1550,6 +1628,15 @@ calculate_add_nonce_with_retry (struct MHD_Connection 
*const connection,
   const uint64_t timestamp1 = MHD_monotonic_msec_counter ();
   const size_t realm_len = strlen (realm);
 
+#ifdef HAVE_MESSAGES
+  if (0 == MHD_get_master (connection->daemon)->digest_auth_rand_size)
+    MHD_DLOG (connection->daemon,
+              _ ("Random value was not initialised by " \
+                 "MHD_OPTION_DIGEST_AUTH_RANDOM or " \
+                 "MHD_OPTION_DIGEST_AUTH_RANDOM_COPY, generated nonces " \
+                 "are predictable.\n"));
+#endif
+
   if (! calculate_add_nonce (connection, timestamp1, realm, realm_len, da,
                              nonce))
   {
@@ -1600,7 +1687,6 @@ calculate_add_nonce_with_retry (struct MHD_Connection 
*const connection,
       return false;
     }
     memcpy (nonce, nonce2, NONCE_STD_LEN (digest_size));
-    mhd_assert (0 == nonce[NONCE_STD_LEN (digest_size)]);
   }
   return true;
 }
@@ -2080,6 +2166,13 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 #endif /* HAVE_MESSAGES */
     return MHD_DAUTH_WRONG_QOP;
   }
+#ifdef HAVE_MESSAGES
+  if ((MHD_DIGEST_AUTH_QOP_NONE == c_qop) &&
+      (0 == (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5)))
+    MHD_DLOG (connection->daemon,
+              _ ("RFC2069 with SHA-256 algorithm is non-standard " \
+                 "extension.\n"));
+#endif /* HAVE_MESSAGES */
 
   digest_size = digest_get_size (&da);
 
@@ -2105,7 +2198,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     return MHD_DAUTH_WRONG_REALM;
   else if (((NULL == userdigest) || params->userhash) &&
            (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len))
-    return MHD_DAUTH_TOO_LARGE; /* Realm is too large and it will be used in 
hash calculations */
+    return MHD_DAUTH_TOO_LARGE; /* Realm is too large and should be used in 
hash calculations */
 
   if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
   {
@@ -2204,7 +2297,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
     digest_update_with_colon (&da);
     digest_update (&da, realm, realm_len);
     digest_calc_hash (&da, hash1_bin);
-    mhd_assert (sizeof (tmp1) >= (2 * digest_size + 1));
+    mhd_assert (sizeof (tmp1) >= (2 * digest_size));
     MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
     if (! is_param_equal_caseless (&params->username, tmp1, 2 * digest_size))
       return MHD_DAUTH_WRONG_USERNAME;
@@ -2298,9 +2391,9 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
                      "probably increase the size of the nonce array.\n"));
       else
         MHD_DLOG (daemon,
-                  _ ("Stale nonce received. If this happens a lot, you should "
-                     "probably increase the size of the nonce array or not"
-                     "use RFC2069-compatible mode .\n"));
+                  _ ("Stale nonce received. This is expected when client " \
+                     "uses RFC2069-compatible mode and makes more than one " \
+                     "request.\n"));
 #endif
       return MHD_DAUTH_NONCE_STALE;
     }
@@ -2359,7 +2452,7 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
 
   digest_init (&da);
   /* Update digest with H(A1) */
-  mhd_assert (sizeof (tmp1) >= (digest_size * 2 + 1));
+  mhd_assert (sizeof (tmp1) >= (digest_size * 2));
   if (NULL == userdigest)
     MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
   else
@@ -2423,25 +2516,33 @@ digest_auth_check_all_inner (struct MHD_Connection 
*connection,
   if (0 != memcmp (hash1_bin, hash2_bin, digest_size))
     return MHD_DAUTH_RESPONSE_WRONG;
 
-  mhd_assert (sizeof(tmp1) >= (NONCE_STD_LEN (digest_size) + 1));
-  /* It was already checked that 'nonce' (including timestamp) was generated
-     by MHD. The next check is mostly an overcaution. */
-  calculate_nonce (nonce_time,
-                   connection->rq.method,
-                   daemon->digest_auth_random,
-                   daemon->digest_auth_rand_size,
-                   connection->rq.url,
-                   connection->rq.url_len,
-                   connection->rq.headers_received,
-                   realm,
-                   realm_len,
-                   &da,
-                   tmp1);
-
-  if (! is_param_equal (&params->nonce, tmp1,
-                        NONCE_STD_LEN (digest_size)))
-    return MHD_DAUTH_NONCE_WRONG;
-  /* The 'nonce' was generated in the same conditions */
+  if (MHD_DAUTH_BIND_NONCE_NONE != daemon->dauth_bind_type)
+  {
+    mhd_assert (sizeof(tmp1) >= (NONCE_STD_LEN (digest_size) + 1));
+    /* It was already checked that 'nonce' (including timestamp) was generated
+       by MHD. */
+    calculate_nonce (nonce_time,
+                     connection->rq.http_mthd,
+                     connection->rq.method,
+                     daemon->digest_auth_random,
+                     daemon->digest_auth_rand_size,
+                     connection->addr,
+                     (size_t) connection->addr_len,
+                     connection->rq.url,
+                     connection->rq.url_len,
+                     connection->rq.headers_received,
+                     realm,
+                     realm_len,
+                     daemon->dauth_bind_type,
+                     &da,
+                     tmp1);
+
+
+    if (! is_param_equal (&params->nonce, tmp1,
+                          NONCE_STD_LEN (digest_size)))
+      return MHD_DAUTH_NONCE_OTHER_COND;
+    /* The 'nonce' was generated in the same conditions */
+  }
 
   return MHD_DAUTH_OK;
 }
@@ -2707,7 +2808,8 @@ MHD_digest_auth_check2 (struct MHD_Connection *connection,
                                 malgo3);
   if (MHD_DAUTH_OK == res)
     return MHD_YES;
-  else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res))
+  else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) ||
+           (MHD_DAUTH_NONCE_OTHER_COND == res) )
     return MHD_INVALID_NONCE;
   return MHD_NO;
 
@@ -2764,7 +2866,8 @@ MHD_digest_auth_check_digest2 (struct MHD_Connection 
*connection,
                                        malgo3);
   if (MHD_DAUTH_OK == res)
     return MHD_YES;
-  else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res))
+  else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) ||
+           (MHD_DAUTH_NONCE_OTHER_COND == res) )
     return MHD_INVALID_NONCE;
   return MHD_NO;
 }
@@ -2918,6 +3021,10 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
       MHD_DLOG (connection->daemon,
                 _ ("The 'userhash' and 'charset' ('prefer_utf8') parameters " \
                    "are not compatible with RFC2069 and ignored.\n"));
+    if (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5))
+      MHD_DLOG (connection->daemon,
+                _ ("RFC2069 with SHA-256 algorithm is non-standard " \
+                   "extension.\n"));
 #endif
     userhash_support = 0;
     prefer_utf8 = 0;
@@ -2940,7 +3047,13 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
   /* 'realm="xxxx", ' */
   realm_len = strlen (realm);
   if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < realm_len)
+  {
+#ifdef HAVE_MESSAGES
+    MHD_DLOG (connection->daemon,
+              _ ("The 'realm' is too large.\n"));
+#endif /* HAVE_MESSAGES */
     return MHD_NO;
+  }
   if ((NULL != memchr (realm, '\r', realm_len)) ||
       (NULL != memchr (realm, '\n', realm_len)))
     return MHD_NO;
@@ -2952,13 +3065,17 @@ MHD_queue_auth_required_response3 (struct 
MHD_Connection *connection,
     buf_size += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_);
   }
   /* 'algorithm="xxxx", ' */
-  buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */
-  if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
-    buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
-  else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
-    buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
-  else
-    mhd_assert (0);
+  if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) ||
+      (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5)))
+  {
+    buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */
+    if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
+      buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
+    else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
+      buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
+    else
+      mhd_assert (0);
+  }
   /* 'nonce="xxxx", ' */
   buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */
   buf_size += NONCE_STD_LEN (digest_get_size (&da)); /* Escaping not needed */
@@ -3016,7 +3133,21 @@ MHD_queue_auth_required_response3 (struct MHD_Connection 
*connection,
           MHD_STATICSTR_LEN_ (prefix_realm));
   p += MHD_STATICSTR_LEN_ (prefix_realm);
   mhd_assert ((buf_size - p) >= (realm_len * 2));
-  p += MHD_str_quote (realm, realm_len, buf + p, buf_size - p);
+  if (1)
+  {
+    size_t quoted_size;
+    quoted_size = MHD_str_quote (realm, realm_len, buf + p, buf_size - p);
+    if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < quoted_size)
+    {
+#ifdef HAVE_MESSAGES
+      MHD_DLOG (connection->daemon,
+                _ ("The 'realm' is too large after 'quoting'.\n"));
+#endif /* HAVE_MESSAGES */
+      free (buf);
+      return MHD_NO;
+    }
+    p += quoted_size;
+  }
   buf[p++] = '\"';
   buf[p++] = ',';
   buf[p++] = ' ';
@@ -3034,23 +3165,27 @@ MHD_queue_auth_required_response3 (struct 
MHD_Connection *connection,
     buf[p++] = ' ';
   }
   /* 'algorithm="xxxx", ' */
-  memcpy (buf + p, prefix_algo,
-          MHD_STATICSTR_LEN_ (prefix_algo));
-  p += MHD_STATICSTR_LEN_ (prefix_algo);
-  if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
+  if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) ||
+      (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5)))
   {
-    memcpy (buf + p, _MHD_MD5_TOKEN,
-            MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN));
-    p += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
-  }
-  else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
-  {
-    memcpy (buf + p, _MHD_SHA256_TOKEN,
-            MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN));
-    p += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
+    memcpy (buf + p, prefix_algo,
+            MHD_STATICSTR_LEN_ (prefix_algo));
+    p += MHD_STATICSTR_LEN_ (prefix_algo);
+    if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
+    {
+      memcpy (buf + p, _MHD_MD5_TOKEN,
+              MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN));
+      p += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
+    }
+    else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
+    {
+      memcpy (buf + p, _MHD_SHA256_TOKEN,
+              MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN));
+      p += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN);
+    }
+    buf[p++] = ',';
+    buf[p++] = ' ';
   }
-  buf[p++] = ',';
-  buf[p++] = ' ';
   /* 'nonce="xxxx", ' */
   memcpy (buf + p, prefix_nonce,
           MHD_STATICSTR_LEN_ (prefix_nonce));
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index e91369ad..3a81b2a2 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -1236,7 +1236,7 @@ struct MHD_Connection
    * Foreign address (of length @e addr_len).  MALLOCED (not
    * in pool!).
    */
-  struct sockaddr *addr;
+  struct sockaddr_storage *addr;
 
 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
   /**
@@ -2225,6 +2225,10 @@ struct MHD_Daemon
    */
   unsigned int nonce_nc_size;
 
+  /**
+   * Nonce bind type.
+   */
+  unsigned int dauth_bind_type;
 #endif
 
 #ifdef TCP_FASTOPEN
diff --git a/src/microhttpd/mhd_str.c b/src/microhttpd/mhd_str.c
index cf2e43c3..6099f93c 100644
--- a/src/microhttpd/mhd_str.c
+++ b/src/microhttpd/mhd_str.c
@@ -1382,11 +1382,24 @@ MHD_bin_to_hex (const void *bin,
     j = b & 0x0f;
     hex[i * 2 + 1] = (char) ((j < 10) ? (j + '0') : (j - 10 + 'a'));
   }
-  hex[i * 2] = 0;
   return i * 2;
 }
 
 
+size_t
+MHD_bin_to_hex_z (const void *bin,
+                  size_t size,
+                  char *hex)
+{
+  size_t res;
+
+  res = MHD_bin_to_hex (bin, size, hex);
+  hex[res] = 0;
+
+  return res;
+}
+
+
 size_t
 MHD_hex_to_bin (const char *hex,
                 size_t len,
diff --git a/src/microhttpd/mhd_str.h b/src/microhttpd/mhd_str.h
index dd74e602..284ad853 100644
--- a/src/microhttpd/mhd_str.h
+++ b/src/microhttpd/mhd_str.h
@@ -497,6 +497,21 @@ MHD_uint8_to_str_pad (uint8_t val,
                       char *buf,
                       size_t buf_size);
 
+
+/**
+ * Convert @a size bytes from input binary data to lower case
+ * hexadecimal digits.
+ * Result is NOT zero-terminated
+ * @param bin the pointer to the binary data to convert
+ * @param size the size in bytes of the binary data to convert
+ * @param[out] hex the output buffer, should be at least 2 * @a size
+ * @return The number of characters written to the output buffer.
+ */
+size_t
+MHD_bin_to_hex (const void *bin,
+                size_t size,
+                char *hex);
+
 /**
  * Convert @a size bytes from input binary data to lower case
  * hexadecimal digits, zero-terminate the result.
@@ -507,9 +522,9 @@ MHD_uint8_to_str_pad (uint8_t val,
  *         not including terminating zero.
  */
 size_t
-MHD_bin_to_hex (const void *bin,
-                size_t size,
-                char *hex);
+MHD_bin_to_hex_z (const void *bin,
+                  size_t size,
+                  char *hex);
 
 /**
  * Convert hexadecimal digits to binary data.
diff --git a/src/microhttpd/test_str_base64.c b/src/microhttpd/test_str_base64.c
index 9daa6cb7..1821d116 100644
--- a/src/microhttpd/test_str_base64.c
+++ b/src/microhttpd/test_str_base64.c
@@ -86,7 +86,7 @@ expect_decoded_n (const char *const encoded, const size_t 
encoded_len,
     }
     else
     {
-      prnt_size = MHD_bin_to_hex (buf, res_size, prnt);
+      prnt_size = MHD_bin_to_hex_z (buf, res_size, prnt);
       mhd_assert (2 * res_size == prnt_size);
 
       fprintf (stderr,
@@ -96,7 +96,7 @@ expect_decoded_n (const char *const encoded, const size_t 
encoded_len,
                (int) prnt_size, prnt, (unsigned) decoded_size,
                (unsigned) res_size);
     }
-    prnt_size = MHD_bin_to_hex (decoded, decoded_size, prnt);
+    prnt_size = MHD_bin_to_hex_z (decoded, decoded_size, prnt);
     mhd_assert (2 * decoded_size == prnt_size);
     fprintf (stderr,
              "\tEXPECTED: MHD_base64_to_bin_n ('%.*s', %u, ->%.*sh, %u)"
@@ -620,7 +620,7 @@ expect_fail_n (const char *const encoded, const size_t 
encoded_len,
     }
     else
     {
-      prnt_size = MHD_bin_to_hex (buf, res_size, prnt);
+      prnt_size = MHD_bin_to_hex_z (buf, res_size, prnt);
       mhd_assert (2 * res_size == prnt_size);
 
       fprintf (stderr,
diff --git a/src/microhttpd/test_str_bin_hex.c 
b/src/microhttpd/test_str_bin_hex.c
index 2b45040a..557146eb 100644
--- a/src/microhttpd/test_str_bin_hex.c
+++ b/src/microhttpd/test_str_bin_hex.c
@@ -168,7 +168,7 @@ expect_decoded_n (const char *const hex, const size_t 
hex_len,
     unsigned int check_res = 0;
 
     memset (buf, fill_chr, sizeof(buf)); /* Fill buffer with some character */
-    res_size = MHD_bin_to_hex (bin, bin_size, buf);
+    res_size = MHD_bin_to_hex_z (bin, bin_size, buf);
 
     if (res_size != hex_len)
     {
diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore
index c06787a1..a31cb1ee 100644
--- a/src/testcurl/.gitignore
+++ b/src/testcurl/.gitignore
@@ -173,3 +173,7 @@ core
 /test_digestauth2_sha256_userdigest
 /test_digestauth2_oldapi2_sha256_userdigest
 /test_digestauth2_sha256_userhash_userdigest
+/test_digestauth2_bind_all
+/test_digestauth2_bind_uri
+/test_digestauth2_oldapi1_bind_all
+/test_digestauth2_oldapi1_bind_uri
diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am
index 34590e8f..0e23f251 100644
--- a/src/testcurl/Makefile.am
+++ b/src/testcurl/Makefile.am
@@ -185,7 +185,11 @@ check_PROGRAMS += \
   test_digestauth2_userhash_userdigest \
   test_digestauth2_sha256_userdigest \
   test_digestauth2_oldapi2_sha256_userdigest \
-  test_digestauth2_sha256_userhash_userdigest
+  test_digestauth2_sha256_userhash_userdigest \
+  test_digestauth2_bind_all \
+  test_digestauth2_bind_uri \
+  test_digestauth2_oldapi1_bind_all \
+  test_digestauth2_oldapi1_bind_uri
 endif
 
 if HEAVY_TESTS
@@ -343,6 +347,18 @@ test_digestauth2_oldapi2_sha256_userdigest_SOURCES = \
 test_digestauth2_sha256_userhash_userdigest_SOURCES = \
   test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
 
+test_digestauth2_bind_all_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_bind_uri_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_oldapi1_bind_all_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
+test_digestauth2_oldapi1_bind_uri_SOURCES = \
+  test_digestauth2.c mhd_has_param.h mhd_has_in_name.h
+
 test_get_iovec_SOURCES = \
   test_get_iovec.c mhd_has_in_name.h
 
diff --git a/src/testcurl/test_digestauth2.c b/src/testcurl/test_digestauth2.c
index 6054af7d..800d57b2 100644
--- a/src/testcurl/test_digestauth2.c
+++ b/src/testcurl/test_digestauth2.c
@@ -228,6 +228,15 @@ _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
 #define TIMEOUTS_VAL 10
 
 #define MHD_URI_BASE_PATH "/bar%20foo?key=value"
+#define MHD_URI_BASE_PATH2 "/another_path"
+/* Should not fit buffer in the stack */
+#define MHD_URI_BASE_PATH3 \
+  "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
+  "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
+  "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
+  "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
+  "/long/long/long/long/long/long/long/long/long/long/long/long/long/long" \
+  "/path?with%20some=parameters"
 
 #define REALM_VAL "TestRealm"
 #define USERNAME1 "test_user"
@@ -274,6 +283,10 @@ static int test_userhash;
 static int test_userdigest;
 static int test_sha256;
 static int test_rfc2069;
+/* Bind DAuth nonces to everything except URI */
+static int test_bind_all;
+/* Bind DAuth nonces to URI */
+static int test_bind_uri;
 static int curl_uses_usehash;
 
 /* Static helper variables */
@@ -388,6 +401,20 @@ struct CBC
   size_t size;
 };
 
+struct req_track
+{
+  /**
+   * The number of used URI, zero-based
+   */
+  unsigned int uri_num;
+
+  /**
+   * The number of request for URI.
+   * This includes number of unauthorised requests.
+   */
+  unsigned int req_num;
+};
+
 
 static size_t
 copyBuffer (void *ptr,
@@ -418,9 +445,10 @@ ahc_echo (void *cls,
   struct MHD_Response *response;
   enum MHD_Result res;
   static int already_called_marker;
-  (void) cls; (void) url;         /* Unused. Silent compiler warning. */
+  struct req_track *const tr_p = (struct req_track *) cls;
+  (void) url;              /* Unused. Silent compiler warning. */
   (void) method; (void) version; (void) upload_data; /* Unused. Silent 
compiler warning. */
-  (void) upload_data_size;        /* Unused. Silent compiler warning. */
+  (void) upload_data_size; /* Unused. Silent compiler warning. */
 
   if (&already_called_marker != *req_cls)
   { /* Called for the first time, request not fully read yet */
@@ -432,6 +460,10 @@ ahc_echo (void *cls,
   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
     mhdErrorExitDesc ("Unexpected HTTP method");
 
+  tr_p->req_num++;
+  if (2 < tr_p->req_num)
+    mhdErrorExitDesc ("Received more than two requests for the same URI");
+
   response = NULL;
   if (! test_oldapi)
   {
@@ -447,6 +479,7 @@ ahc_echo (void *cls,
       /* Got any kind of Digest response. Check it, it must be valid */
       struct MHD_DigestAuthUsernameInfo *uname;
       enum MHD_DigestAuthResult check_res;
+      enum MHD_DigestAuthResult expect_res;
 
       if (NULL == dinfo->username)
         mhdErrorExitDesc ("'username' is NULL");
@@ -678,18 +711,58 @@ ahc_echo (void *cls,
                                          (enum MHD_DigestAuthMultiQOP) qop,
                                          (enum MHD_DigestAuthMultiAlgo3) 
algo3);
 
+      if (test_rfc2069)
+      {
+        if ((0 != tr_p->uri_num) && (1 == tr_p->req_num))
+          expect_res = MHD_DAUTH_NONCE_STALE;
+        else
+          expect_res = MHD_DAUTH_OK;
+      }
+      else if (test_bind_uri)
+      {
+        if ((0 != tr_p->uri_num) && (1 == tr_p->req_num))
+          expect_res = MHD_DAUTH_NONCE_OTHER_COND;
+        else
+          expect_res = MHD_DAUTH_OK;
+      }
+      else
+        expect_res = MHD_DAUTH_OK;
+
       switch (check_res)
       {
-      /* Valid result */
+      /* Conditionally valid results */
       case MHD_DAUTH_OK:
-        if (verbose)
-          printf ("Got valid auth check result: MHD_DAUTH_OK.\n");
+        if (expect_res == MHD_DAUTH_OK)
+        {
+          if (verbose)
+            printf ("Got valid auth check result: MHD_DAUTH_OK.\n");
+        }
+        else
+          mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
+                            "MHD_DAUTH_OK");
         break;
-      /* Invalid results */
       case MHD_DAUTH_NONCE_STALE:
-        mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
-                          "MHD_DAUTH_NONCE_STALE");
+        if (expect_res == MHD_DAUTH_NONCE_STALE)
+        {
+          if (verbose)
+            printf ("Got expected auth check result: 
MHD_DAUTH_NONCE_STALE.\n");
+        }
+        else
+          mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
+                            "MHD_DAUTH_NONCE_STALE");
         break;
+      case MHD_DAUTH_NONCE_OTHER_COND:
+        if (expect_res == MHD_DAUTH_NONCE_OTHER_COND)
+        {
+          if (verbose)
+            printf ("Got expected auth check result: "
+                    "MHD_DAUTH_NONCE_OTHER_COND.\n");
+        }
+        else
+          mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
+                            "MHD_DAUTH_NONCE_OTHER_COND");
+        break;
+      /* Invalid results */
       case MHD_DAUTH_NONCE_WRONG:
         mhdErrorExitDesc ("MHD_digest_auth_check[_digest]3()' returned " \
                           "MHD_DAUTH_NONCE_WRONG");
@@ -726,20 +799,54 @@ ahc_echo (void *cls,
                  check_res);
         mhdErrorExitDesc ("Impossible returned code");
       }
+      fflush (stderr);
+      fflush (stdout);
 
-      response =
-        MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
-                                                (const void *) PAGE);
-      if (NULL == response)
-        mhdErrorExitDesc ("Response creation failed");
-
-      if (MHD_YES !=
-          MHD_queue_response (connection, MHD_HTTP_OK, response))
-        mhdErrorExitDesc ("'MHD_queue_response()' failed");
+      if (MHD_DAUTH_OK == check_res)
+      {
+        response =
+          MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
+                                                  (const void *) PAGE);
+        if (NULL == response)
+          mhdErrorExitDesc ("Response creation failed");
+
+        if (MHD_YES !=
+            MHD_queue_response (connection, MHD_HTTP_OK, response))
+          mhdErrorExitDesc ("'MHD_queue_response()' failed");
+      }
+      else if ((MHD_DAUTH_NONCE_STALE == check_res) ||
+               (MHD_DAUTH_NONCE_OTHER_COND == check_res))
+      {
+        response =
+          MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
+                                                  (const void *) DENIED);
+        if (NULL == response)
+          mhdErrorExitDesc ("Response creation failed");
+        res =
+          MHD_queue_auth_required_response3 (connection, REALM_VAL,
+                                             OPAQUE_VALUE,
+                                             "/", response, 1,
+                                             (enum MHD_DigestAuthMultiQOP) qop,
+                                             (enum MHD_DigestAuthMultiAlgo3)
+                                             algo3,
+                                             test_userhash, 0);
+        if (MHD_YES != res)
+          mhdErrorExitDesc ("'MHD_queue_auth_required_response3()' failed");
+      }
+      else
+        externalErrorExitDesc ("Wrong 'check_res' value");
     }
     else
     {
       /* No Digest auth header */
+      if ((1 != tr_p->req_num) || (0 != tr_p->uri_num))
+      {
+        fprintf (stderr, "Received request number %u for URI number %u "
+                 "without Digest Authorisation header. ",
+                 tr_p->req_num, tr_p->uri_num + 1);
+        mhdErrorExitDesc ("Wrong requests sequence");
+      }
+
       response =
         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
                                                 (const void *) DENIED);
@@ -760,6 +867,7 @@ ahc_echo (void *cls,
     /* Use old API v2 */
     char *username;
     int check_res;
+    int expect_res;
 
     username = MHD_digest_auth_get_username (connection);
     if (NULL != username)
@@ -790,10 +898,21 @@ ahc_echo (void *cls,
                                          MHD_DIGEST_ALG_SHA256 :
                                          MHD_DIGEST_ALG_MD5);
 
-      if (MHD_YES != check_res)
+      if (test_bind_uri)
+      {
+        if ((0 != tr_p->uri_num) && (1 == tr_p->req_num))
+          expect_res = MHD_INVALID_NONCE;
+        else
+          expect_res = MHD_YES;
+      }
+      else
+        expect_res = MHD_YES;
+
+      if (expect_res != check_res)
       {
         fprintf (stderr, "'MHD_digest_auth_check[_digest]2()' returned "
-                 "unexpected result: %d. ", check_res);
+                 "unexpected result '%d', while expected is '%d. ",
+                 check_res, expect_res);
         mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check[_digest]2()' result");
       }
       response =
@@ -802,13 +921,35 @@ ahc_echo (void *cls,
       if (NULL == response)
         mhdErrorExitDesc ("Response creation failed");
 
-      if (MHD_YES !=
-          MHD_queue_response (connection, MHD_HTTP_OK, response))
-        mhdErrorExitDesc ("'MHD_queue_response()' failed");
+      if (MHD_YES == expect_res)
+      {
+        if (MHD_YES !=
+            MHD_queue_response (connection, MHD_HTTP_OK, response))
+          mhdErrorExitDesc ("'MHD_queue_response()' failed");
+      }
+      else if (MHD_INVALID_NONCE == expect_res)
+      {
+        if (MHD_YES !=
+            MHD_queue_auth_fail_response2 (connection, REALM_VAL, OPAQUE_VALUE,
+                                           response, 1,
+                                           test_sha256 ?
+                                           MHD_DIGEST_ALG_SHA256 :
+                                           MHD_DIGEST_ALG_MD5))
+          mhdErrorExitDesc ("'MHD_queue_auth_fail_response2()' failed");
+      }
+      else
+        externalErrorExitDesc ("Wrong 'check_res' value");
     }
     else
     {
       /* Has no valid username in header */
+      if ((1 != tr_p->req_num) || (0 != tr_p->uri_num))
+      {
+        fprintf (stderr, "Received request number %u for URI number %u "
+                 "without Digest Authorisation header. ",
+                 tr_p->req_num, tr_p->uri_num + 1);
+        mhdErrorExitDesc ("Wrong requests sequence");
+      }
       response =
         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
                                                 (const void *) DENIED);
@@ -821,7 +962,7 @@ ahc_echo (void *cls,
                                            MHD_DIGEST_ALG_SHA256 :
                                            MHD_DIGEST_ALG_MD5);
       if (MHD_YES != res)
-        mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed");
+        mhdErrorExitDesc ("'MHD_queue_auth_fail_response2()' failed");
     }
   }
   else if (1 == test_oldapi)
@@ -829,6 +970,7 @@ ahc_echo (void *cls,
     /* Use old API v1 */
     char *username;
     int check_res;
+    int expect_res;
 
     username = MHD_digest_auth_get_username (connection);
     if (NULL != username)
@@ -854,25 +996,56 @@ ahc_echo (void *cls,
                                         userdigest_bin,
                                         50 * TIMEOUTS_VAL);
 
-      if (MHD_YES != check_res)
+      if (test_bind_uri)
+      {
+        if ((0 != tr_p->uri_num) && (1 == tr_p->req_num))
+          expect_res = MHD_INVALID_NONCE;
+        else
+          expect_res = MHD_YES;
+      }
+      else
+        expect_res = MHD_YES;
+
+      if (expect_res != check_res)
       {
         fprintf (stderr, "'MHD_digest_auth_check[_digest]()' returned "
-                 "unexpected result: %d. ", check_res);
+                 "unexpected result '%d', while expected is '%d. ",
+                 check_res, expect_res);
         mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check[_digest]()' result");
       }
+
       response =
         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
                                                 (const void *) PAGE);
       if (NULL == response)
         mhdErrorExitDesc ("Response creation failed");
 
-      if (MHD_YES !=
-          MHD_queue_response (connection, MHD_HTTP_OK, response))
-        mhdErrorExitDesc ("'MHD_queue_response()' failed");
+      if (MHD_YES == expect_res)
+      {
+        if (MHD_YES !=
+            MHD_queue_response (connection, MHD_HTTP_OK, response))
+          mhdErrorExitDesc ("'MHD_queue_response()' failed");
+      }
+      else if (MHD_INVALID_NONCE == expect_res)
+      {
+        if (MHD_YES !=
+            MHD_queue_auth_fail_response (connection, REALM_VAL, OPAQUE_VALUE,
+                                          response, 1))
+          mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed");
+      }
+      else
+        externalErrorExitDesc ("Wrong 'check_res' value");
     }
     else
     {
       /* Has no valid username in header */
+      if ((1 != tr_p->req_num) || (0 != tr_p->uri_num))
+      {
+        fprintf (stderr, "Received request number %u for URI number %u "
+                 "without Digest Authorisation header. ",
+                 tr_p->req_num, tr_p->uri_num + 1);
+        mhdErrorExitDesc ("Wrong requests sequence");
+      }
       response =
         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
                                                 (const void *) DENIED);
@@ -893,22 +1066,42 @@ ahc_echo (void *cls,
 }
 
 
+/**
+ *
+ * @param c the CURL handle to use
+ * @param port the port to set
+ * @param uri_num the number of URI, should be 0, 1 or 2
+ */
+static void
+setCURL_rq_path (CURL *c, unsigned int port, unsigned int uri_num)
+{
+  const char *req_path;
+  char uri[512];
+  int res;
+
+  if (0 == uri_num)
+    req_path = MHD_URI_BASE_PATH;
+  else if (1 == uri_num)
+    req_path = MHD_URI_BASE_PATH2;
+  else
+    req_path = MHD_URI_BASE_PATH3;
+  /* A workaround for some old libcurl versions, which ignore the specified
+   * port by CURLOPT_PORT when authorisation is used. */
+  res = snprintf (uri, (sizeof(uri) / sizeof(uri[0])),
+                  "http://127.0.0.1:%u%s";, port,
+                  req_path);
+  if ((0 >= res) || ((sizeof(uri) / sizeof(uri[0])) <= (size_t) res))
+    externalErrorExitDesc ("Cannot form request URL");
+
+  if (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, uri))
+    libcurlErrorExitDesc ("Cannot set request URL");
+}
+
+
 static CURL *
-setupCURL (void *cbc, int port)
+setupCURL (void *cbc, unsigned int port)
 {
   CURL *c;
-  char url[512];
-
-  if (1)
-  {
-    int res;
-    /* A workaround for some old libcurl versions, which ignore the specified
-     * port by CURLOPT_PORT when authorisation is used. */
-    res = snprintf (url, (sizeof(url) / sizeof(url[0])),
-                    "http://127.0.0.1:%d%s";, port, MHD_URI_BASE_PATH);
-    if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
-      externalErrorExitDesc ("Cannot form request URL");
-  }
 
   c = curl_easy_init ();
   if (NULL == c)
@@ -943,15 +1136,17 @@ setupCURL (void *cbc, int port)
 #if CURL_AT_LEAST_VERSION (7, 45, 0)
       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
-      (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) ||
-      (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url)))
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))))
     libcurlErrorExitDesc ("curl_easy_setopt() failed");
+
+  setCURL_rq_path (c, port, 0);
+
   return c;
 }
 
 
 static CURLcode
-performQueryExternal (struct MHD_Daemon *d, CURL *c)
+performQueryExternal (struct MHD_Daemon *d, CURL *c, CURLM **multi_reuse)
 {
   CURLM *multi;
   time_t start;
@@ -959,10 +1154,15 @@ performQueryExternal (struct MHD_Daemon *d, CURL *c)
   CURLcode ret;
 
   ret = CURLE_FAILED_INIT; /* will be replaced with real result */
-  multi = NULL;
-  multi = curl_multi_init ();
-  if (multi == NULL)
-    libcurlErrorExitDesc ("curl_multi_init() failed");
+  if (NULL != *multi_reuse)
+    multi = *multi_reuse;
+  else
+  {
+    multi = curl_multi_init ();
+    if (multi == NULL)
+      libcurlErrorExitDesc ("curl_multi_init() failed");
+    *multi_reuse = multi;
+  }
   if (CURLM_OK != curl_multi_add_handle (multi, c))
     libcurlErrorExitDesc ("curl_multi_add_handle() failed");
 
@@ -1007,7 +1207,6 @@ performQueryExternal (struct MHD_Daemon *d, CURL *c)
           externalErrorExit ();
         }
         curl_multi_remove_handle (multi, c);
-        curl_multi_cleanup (multi);
         multi = NULL;
       }
       else
@@ -1106,11 +1305,14 @@ check_result (CURLcode curl_code, CURL *c, struct CBC 
*pcbc)
 static unsigned int
 testDigestAuth (void)
 {
+  unsigned int dauth_nonce_bind;
   struct MHD_Daemon *d;
   uint16_t port;
   struct CBC cbc;
+  struct req_track rq_tr;
   char buf[2048];
   CURL *c;
+  CURLM *multi_reuse;
   int failed = 0;
 
   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
@@ -1129,12 +1331,21 @@ testDigestAuth (void)
       fflush (stderr);
     }
 
+    dauth_nonce_bind = MHD_DAUTH_BIND_NONCE_NONE;
+    if (test_bind_all)
+      dauth_nonce_bind |=
+        (MHD_DAUTH_BIND_NONCE_CLIENT_IP | MHD_DAUTH_BIND_NONCE_REALM);
+    if (test_bind_uri)
+      dauth_nonce_bind |= MHD_DAUTH_BIND_NONCE_URI_PARAMS;
+
     d = MHD_start_daemon (MHD_USE_ERROR_LOG,
                           port, NULL, NULL,
-                          &ahc_echo, NULL,
+                          &ahc_echo, &rq_tr,
                           MHD_OPTION_DIGEST_AUTH_RANDOM_COPY,
                           sizeof (salt), salt,
                           MHD_OPTION_NONCE_NC_SIZE, 300,
+                          MHD_OPTION_DIGEST_AUTH_NONCE_BIND_TYPE,
+                          dauth_nonce_bind,
                           MHD_OPTION_END);
   }
   if (d == NULL)
@@ -1152,22 +1363,66 @@ testDigestAuth (void)
   }
 
   /* First request */
+  rq_tr.req_num = 0;
+  rq_tr.uri_num = 0;
   cbc.buf = buf;
   cbc.size = sizeof (buf);
   cbc.pos = 0;
   memset (cbc.buf, 0, cbc.size);
-  c = setupCURL (&cbc, port);
-  if (check_result (performQueryExternal (d, c), c, &cbc))
+  c = setupCURL (&cbc, (unsigned int) port);
+  multi_reuse = NULL;
+  /* First request */
+  if (check_result (performQueryExternal (d, c, &multi_reuse), c, &cbc))
   {
+    fflush (stderr);
     if (verbose)
-      printf ("Got expected response.\n");
+      printf ("Got first expected response.\n");
+    fflush (stdout);
   }
   else
   {
-    fprintf (stderr, "Request FAILED.\n");
+    fprintf (stderr, "First request FAILED.\n");
     failed = 1;
   }
+  cbc.pos = 0; /* Reset buffer position */
+  rq_tr.req_num = 0;
+  /* Second request */
+  setCURL_rq_path (c, port, ++rq_tr.uri_num);
+  if (check_result (performQueryExternal (d, c, &multi_reuse), c, &cbc))
+  {
+    fflush (stderr);
+    if (verbose)
+      printf ("Got second expected response.\n");
+    fflush (stdout);
+  }
+  else
+  {
+    fprintf (stderr, "Second request FAILED.\n");
+    failed = 1;
+  }
+  cbc.pos = 0; /* Reset buffer position */
+  rq_tr.req_num = 0;
+  /* Third request */
+  if (NULL != multi_reuse)
+    curl_multi_cleanup (multi_reuse);
+  multi_reuse = NULL; /* Force new connection */
+  setCURL_rq_path (c, port, ++rq_tr.uri_num);
+  if (check_result (performQueryExternal (d, c, &multi_reuse), c, &cbc))
+  {
+    fflush (stderr);
+    if (verbose)
+      printf ("Got third expected response.\n");
+    fflush (stdout);
+  }
+  else
+  {
+    fprintf (stderr, "Third request FAILED.\n");
+    failed = 1;
+  }
+
   curl_easy_cleanup (c);
+  if (NULL != multi_reuse)
+    curl_multi_cleanup (multi_reuse);
 
   MHD_stop_daemon (d);
   return failed ? 1 : 0;
@@ -1204,6 +1459,8 @@ main (int argc, char *const *argv)
   test_userdigest = has_in_name (argv[0], "_userdigest");
   test_sha256 = has_in_name (argv[0], "_sha256");
   test_rfc2069 = has_in_name (argv[0], "_rfc2069");
+  test_bind_all = has_in_name (argv[0], "_bind_all");
+  test_bind_uri = has_in_name (argv[0], "_bind_uri");
 
   /* Wrong test types combinations */
   if (1 == test_oldapi)
diff --git a/src/testcurl/test_digestauth_concurrent.c 
b/src/testcurl/test_digestauth_concurrent.c
index 6d4e591c..efda16b6 100644
--- a/src/testcurl/test_digestauth_concurrent.c
+++ b/src/testcurl/test_digestauth_concurrent.c
@@ -383,6 +383,9 @@ setupCURL (void *cbc, uint16_t port, char *errbuf)
       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
                                      CURL_HTTP_VERSION_1_1)) ||
       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
+#ifdef _DEBUG
+      (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) ||
+#endif /* _DEBUG */
 #if CURL_AT_LEAST_VERSION (7, 19, 4)
       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
@@ -472,7 +475,6 @@ worker_func (void *param)
   req_result = curl_easy_perform (w->c);
   if (CURLE_OK != req_result)
   {
-    fflush (stdout);
     if (0 != w->libcurl_errbuf[0])
       fprintf (stderr, "Worker %d: first request failed. "
                "libcurl error: '%s'.\n"
@@ -483,7 +485,6 @@ worker_func (void *param)
       fprintf (stderr, "Worker %d: first request failed. "
                "libcurl error: '%s'.\n",
                w->workerNumber, curl_easy_strerror (req_result));
-    fflush (stderr);
   }
   else
   {
@@ -506,13 +507,16 @@ worker_func (void *param)
       printf ("Worker %d: first request successful.\n", w->workerNumber);
     w->success++;
   }
+#ifdef _DEBUG
+  fflush (stderr);
+  fflush (stdout);
+#endif /* _DEBUG */
 
   /* Second request */
   w->cbc.pos = 0;
   req_result = curl_easy_perform (w->c);
   if (CURLE_OK != req_result)
   {
-    fflush (stdout);
     if (0 != w->libcurl_errbuf[0])
       fprintf (stderr, "Worker %d: second request failed. "
                "libcurl error: '%s'.\n"
@@ -523,7 +527,6 @@ worker_func (void *param)
       fprintf (stderr, "Worker %d: second request failed. "
                "libcurl error: '%s'.\n",
                w->workerNumber, curl_easy_strerror (req_result));
-    fflush (stderr);
   }
   else
   {
@@ -546,6 +549,10 @@ worker_func (void *param)
       printf ("Worker %d: second request successful.\n", w->workerNumber);
     w->success++;
   }
+#ifdef _DEBUG
+  fflush (stderr);
+  fflush (stdout);
+#endif /* _DEBUG */
 
   w->finished = ! 0;
   return NULL;

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]