libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

test_digestauth_emu_ext.c (28694B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2010 Christian Grothoff
      4      Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 2, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file test_digest_emu_ext.c
     24  * @brief  Testcase for MHD Digest Authorisation client's header parsing
     25  * @author Karlson2k (Evgeny Grin)
     26  *
     27  * libcurl does not support extended notation for username, so this test
     28  * "emulates" client request will all valid fields except nonce, cnonce and
     29  * response (however syntactically these fields valid as well).
     30  */
     31 
     32 #include "MHD_config.h"
     33 #include "platform.h"
     34 #include <curl/curl.h>
     35 #include <microhttpd.h>
     36 #include <stdlib.h>
     37 #include <string.h>
     38 #include <time.h>
     39 #include <errno.h>
     40 
     41 #ifndef WINDOWS
     42 #include <sys/socket.h>
     43 #include <unistd.h>
     44 #else
     45 #include <wincrypt.h>
     46 #endif
     47 
     48 #include "mhd_has_param.h"
     49 #include "mhd_has_in_name.h"
     50 
     51 #ifndef MHD_STATICSTR_LEN_
     52 /**
     53  * Determine length of static string / macro strings at compile time.
     54  */
     55 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     56 #endif /* ! MHD_STATICSTR_LEN_ */
     57 
     58 #ifndef CURL_VERSION_BITS
     59 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
     60 #endif /* ! CURL_VERSION_BITS */
     61 #ifndef CURL_AT_LEAST_VERSION
     62 #define CURL_AT_LEAST_VERSION(x,y,z) \
     63   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
     64 #endif /* ! CURL_AT_LEAST_VERSION */
     65 
     66 #ifndef _MHD_INSTRMACRO
     67 /* Quoted macro parameter */
     68 #define _MHD_INSTRMACRO(a) #a
     69 #endif /* ! _MHD_INSTRMACRO */
     70 #ifndef _MHD_STRMACRO
     71 /* Quoted expanded macro parameter */
     72 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
     73 #endif /* ! _MHD_STRMACRO */
     74 
     75 #if defined(HAVE___FUNC__)
     76 #define externalErrorExit(ignore) \
     77   _externalErrorExit_func (NULL, __func__, __LINE__)
     78 #define externalErrorExitDesc(errDesc) \
     79   _externalErrorExit_func (errDesc, __func__, __LINE__)
     80 #define libcurlErrorExit(ignore) \
     81   _libcurlErrorExit_func (NULL, __func__, __LINE__)
     82 #define libcurlErrorExitDesc(errDesc) \
     83   _libcurlErrorExit_func (errDesc, __func__, __LINE__)
     84 #define mhdErrorExit(ignore) \
     85   _mhdErrorExit_func (NULL, __func__, __LINE__)
     86 #define mhdErrorExitDesc(errDesc) \
     87   _mhdErrorExit_func (errDesc, __func__, __LINE__)
     88 #define checkCURLE_OK(libcurlcall) \
     89   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
     90                        __func__, __LINE__)
     91 #elif defined(HAVE___FUNCTION__)
     92 #define externalErrorExit(ignore) \
     93   _externalErrorExit_func (NULL, __FUNCTION__, __LINE__)
     94 #define externalErrorExitDesc(errDesc) \
     95   _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__)
     96 #define libcurlErrorExit(ignore) \
     97   _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__)
     98 #define libcurlErrorExitDesc(errDesc) \
     99   _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    100 #define mhdErrorExit(ignore) \
    101   _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__)
    102 #define mhdErrorExitDesc(errDesc) \
    103   _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    104 #define checkCURLE_OK(libcurlcall) \
    105   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
    106                        __FUNCTION__, __LINE__)
    107 #else
    108 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__)
    109 #define externalErrorExitDesc(errDesc) \
    110   _externalErrorExit_func (errDesc, NULL, __LINE__)
    111 #define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__)
    112 #define libcurlErrorExitDesc(errDesc) \
    113   _libcurlErrorExit_func (errDesc, NULL, __LINE__)
    114 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__)
    115 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__)
    116 #define checkCURLE_OK(libcurlcall) \
    117   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \
    118                        __LINE__)
    119 #endif
    120 
    121 
    122 _MHD_NORETURN static void
    123 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    124 {
    125   fflush (stdout);
    126   if ((NULL != errDesc) && (0 != errDesc[0]))
    127     fprintf (stderr, "%s", errDesc);
    128   else
    129     fprintf (stderr, "System or external library call failed");
    130   if ((NULL != funcName) && (0 != funcName[0]))
    131     fprintf (stderr, " in %s", funcName);
    132   if (0 < lineNum)
    133     fprintf (stderr, " at line %d", lineNum);
    134 
    135   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    136            strerror (errno));
    137 #ifdef MHD_WINSOCK_SOCKETS
    138   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    139 #endif /* MHD_WINSOCK_SOCKETS */
    140   fflush (stderr);
    141   exit (99);
    142 }
    143 
    144 
    145 /* Not actually used in this test */
    146 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    147 
    148 _MHD_NORETURN static void
    149 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    150 {
    151   fflush (stdout);
    152   if ((NULL != errDesc) && (0 != errDesc[0]))
    153     fprintf (stderr, "%s", errDesc);
    154   else
    155     fprintf (stderr, "CURL library call failed");
    156   if ((NULL != funcName) && (0 != funcName[0]))
    157     fprintf (stderr, " in %s", funcName);
    158   if (0 < lineNum)
    159     fprintf (stderr, " at line %d", lineNum);
    160 
    161   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    162            strerror (errno));
    163 #ifdef MHD_WINSOCK_SOCKETS
    164   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    165 #endif /* MHD_WINSOCK_SOCKETS */
    166   if (0 != libcurl_errbuf[0])
    167     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    168 
    169   fflush (stderr);
    170   exit (99);
    171 }
    172 
    173 
    174 _MHD_NORETURN static void
    175 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    176 {
    177   fflush (stdout);
    178   if ((NULL != errDesc) && (0 != errDesc[0]))
    179     fprintf (stderr, "%s", errDesc);
    180   else
    181     fprintf (stderr, "MHD unexpected error");
    182   if ((NULL != funcName) && (0 != funcName[0]))
    183     fprintf (stderr, " in %s", funcName);
    184   if (0 < lineNum)
    185     fprintf (stderr, " at line %d", lineNum);
    186 
    187   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    188            strerror (errno));
    189 #ifdef MHD_WINSOCK_SOCKETS
    190   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    191 #endif /* MHD_WINSOCK_SOCKETS */
    192 
    193   fflush (stderr);
    194   exit (8);
    195 }
    196 
    197 
    198 #if 0
    199 /* Function unused in this test */
    200 static void
    201 _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
    202                      const char *funcName, int lineNum)
    203 {
    204   if (CURLE_OK == code)
    205     return;
    206 
    207   fflush (stdout);
    208   if ((NULL != curlFunc) && (0 != curlFunc[0]))
    209     fprintf (stderr, "'%s' resulted in '%s'", curlFunc,
    210              curl_easy_strerror (code));
    211   else
    212     fprintf (stderr, "libcurl function call resulted in '%s'",
    213              curl_easy_strerror (code));
    214   if ((NULL != funcName) && (0 != funcName[0]))
    215     fprintf (stderr, " in %s", funcName);
    216   if (0 < lineNum)
    217     fprintf (stderr, " at line %d", lineNum);
    218 
    219   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    220            strerror (errno));
    221   if (0 != libcurl_errbuf[0])
    222     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    223 
    224   fflush (stderr);
    225   exit (9);
    226 }
    227 
    228 
    229 #endif
    230 
    231 
    232 /* Could be increased to facilitate debugging */
    233 #define TIMEOUTS_VAL 10
    234 
    235 #define MHD_URI_BASE_PATH "/bar%20foo?key=value"
    236 
    237 #define REALM "TestRealm"
    238 /* "titkos szuperügynök" in UTF-8 */
    239 #define USERNAME "titkos szuper" "\xC3\xBC" "gyn" "\xC3\xB6" "k"
    240 /* percent-encoded username */
    241 #define USERNAME_PCTENC "titkos%20szuper%C3%BCgyn%C3%B6k"
    242 #define PASSWORD_VALUE "fake pass"
    243 #define OPAQUE_VALUE "opaque-content"
    244 #define NONCE_EMU "badbadbadbadbadbadbadbadbadbadbadbadbadbadba"
    245 #define CNONCE_EMU "utututututututututututututututututututututs="
    246 #define RESPONSE_EMU "badbadbadbadbadbadbadbadbadbadba"
    247 
    248 
    249 #define PAGE \
    250   "<html><head><title>libmicrohttpd demo page</title>" \
    251   "</head><body>Access granted</body></html>"
    252 
    253 #define DENIED \
    254   "<html><head><title>libmicrohttpd - Access denied</title>" \
    255   "</head><body>Access denied</body></html>"
    256 
    257 struct CBC
    258 {
    259   char *buf;
    260   size_t pos;
    261   size_t size;
    262 };
    263 
    264 /* Global parameters */
    265 static int verbose;
    266 static int oldapi;
    267 
    268 /* Static helper variables */
    269 static struct curl_slist *curl_headers;
    270 
    271 static void
    272 test_global_init (void)
    273 {
    274   libcurl_errbuf[0] = 0;
    275 
    276   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    277     externalErrorExit ();
    278 
    279   curl_headers = NULL;
    280   curl_headers =
    281     curl_slist_append (curl_headers,
    282                        "Authorization: "
    283                        "Digest username*=UTF-8''" USERNAME_PCTENC ", "
    284                        "realm=\"" REALM "\", "
    285                        "nonce=\"" NONCE_EMU "\", "
    286                        "uri=\"" MHD_URI_BASE_PATH "\", "
    287                        "cnonce=\"" CNONCE_EMU "\", "
    288                        "nc=00000001, "
    289                        "qop=auth, "
    290                        "response=\"" RESPONSE_EMU "\", "
    291                        "opaque=\"" OPAQUE_VALUE "\", "
    292                        "algorithm=MD5");
    293   if (NULL == curl_headers)
    294     externalErrorExit ();
    295 }
    296 
    297 
    298 static void
    299 test_global_cleanup (void)
    300 {
    301   curl_slist_free_all (curl_headers);
    302   curl_headers = NULL;
    303   curl_global_cleanup ();
    304 }
    305 
    306 
    307 static size_t
    308 copyBuffer (void *ptr,
    309             size_t size,
    310             size_t nmemb,
    311             void *ctx)
    312 {
    313   struct CBC *cbc = ctx;
    314 
    315   if (cbc->pos + size * nmemb > cbc->size)
    316     mhdErrorExitDesc ("Wrong too large data");       /* overflow */
    317   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    318   cbc->pos += size * nmemb;
    319   return size * nmemb;
    320 }
    321 
    322 
    323 static enum MHD_Result
    324 ahc_echo (void *cls,
    325           struct MHD_Connection *connection,
    326           const char *url,
    327           const char *method,
    328           const char *version,
    329           const char *upload_data,
    330           size_t *upload_data_size,
    331           void **req_cls)
    332 {
    333   struct MHD_Response *response;
    334   enum MHD_Result ret;
    335   static int already_called_marker;
    336   (void) cls; (void) url;         /* Unused. Silent compiler warning. */
    337   (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */
    338   (void) upload_data_size;        /* Unused. Silent compiler warning. */
    339 
    340   if (&already_called_marker != *req_cls)
    341   { /* Called for the first time, request not fully read yet */
    342     *req_cls = &already_called_marker;
    343     /* Wait for complete request */
    344     return MHD_YES;
    345   }
    346 
    347   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
    348     mhdErrorExitDesc ("Unexpected HTTP method");
    349 
    350   if (! oldapi)
    351   {
    352     struct MHD_DigestAuthUsernameInfo *creds;
    353     struct MHD_DigestAuthInfo *dinfo;
    354     enum MHD_DigestAuthResult check_res;
    355 
    356     creds = MHD_digest_auth_get_username3 (connection);
    357     if (NULL == creds)
    358       mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL");
    359     else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != creds->uname_type)
    360     {
    361       fprintf (stderr, "Unexpected 'uname_type'.\n"
    362                "Expected: %d\tRecieved: %d. ",
    363                (int) MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED,
    364                (int) creds->uname_type);
    365       mhdErrorExitDesc ("Wrong 'uname_type'");
    366     }
    367     else if (NULL == creds->username)
    368       mhdErrorExitDesc ("'username' is NULL");
    369     else if (creds->username_len != MHD_STATICSTR_LEN_ (USERNAME))
    370     {
    371       fprintf (stderr, "'username_len' does not match.\n"
    372                "Expected: %u\tRecieved: %u. ",
    373                (unsigned) MHD_STATICSTR_LEN_ (USERNAME),
    374                (unsigned) creds->username_len);
    375       mhdErrorExitDesc ("Wrong 'username_len'");
    376     }
    377     else if (0 != memcmp (creds->username, USERNAME, creds->username_len))
    378     {
    379       fprintf (stderr, "'username' does not match.\n"
    380                "Expected: '%s'\tRecieved: '%.*s'. ",
    381                USERNAME,
    382                (int) creds->username_len,
    383                creds->username);
    384       mhdErrorExitDesc ("Wrong 'username'");
    385     }
    386     else if (NULL != creds->userhash_hex)
    387       mhdErrorExitDesc ("'userhash_hex' is NOT NULL");
    388     else if (0 != creds->userhash_hex_len)
    389       mhdErrorExitDesc ("'userhash_hex' is NOT zero");
    390     else if (NULL != creds->userhash_bin)
    391       mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
    392 
    393     dinfo = MHD_digest_auth_get_request_info3 (connection);
    394     if (NULL == dinfo)
    395       mhdErrorExitDesc ("MHD_digest_auth_get_username3() returned NULL");
    396     else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != dinfo->uname_type)
    397     {
    398       fprintf (stderr, "Unexpected 'uname_type'.\n"
    399                "Expected: %d\tRecieved: %d. ",
    400                (int) MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED,
    401                (int) creds->uname_type);
    402       mhdErrorExitDesc ("Wrong 'uname_type'");
    403     }
    404     else if (NULL == dinfo->username)
    405       mhdErrorExitDesc ("'username' is NULL");
    406     else if (dinfo->username_len != MHD_STATICSTR_LEN_ (USERNAME))
    407     {
    408       fprintf (stderr, "'username_len' does not match.\n"
    409                "Expected: %u\tRecieved: %u. ",
    410                (unsigned) MHD_STATICSTR_LEN_ (USERNAME),
    411                (unsigned) dinfo->username_len);
    412       mhdErrorExitDesc ("Wrong 'username_len'");
    413     }
    414     else if (0 != memcmp (dinfo->username, USERNAME, dinfo->username_len))
    415     {
    416       fprintf (stderr, "'username' does not match.\n"
    417                "Expected: '%s'\tRecieved: '%.*s'. ",
    418                USERNAME,
    419                (int) dinfo->username_len,
    420                dinfo->username);
    421       mhdErrorExitDesc ("Wrong 'username'");
    422     }
    423     else if (NULL != dinfo->userhash_hex)
    424       mhdErrorExitDesc ("'userhash_hex' is NOT NULL");
    425     else if (0 != dinfo->userhash_hex_len)
    426       mhdErrorExitDesc ("'userhash_hex' is NOT zero");
    427     else if (NULL != dinfo->userhash_bin)
    428       mhdErrorExitDesc ("'userhash_bin' is NOT NULL");
    429     else if (MHD_DIGEST_AUTH_ALGO3_MD5 != dinfo->algo3)
    430     {
    431       fprintf (stderr, "Unexpected 'algo'.\n"
    432                "Expected: %d\tRecieved: %d. ",
    433                (int) MHD_DIGEST_AUTH_ALGO3_MD5,
    434                (int) dinfo->algo3);
    435       mhdErrorExitDesc ("Wrong 'algo'");
    436     }
    437     else if (MHD_STATICSTR_LEN_ (CNONCE_EMU) != dinfo->cnonce_len)
    438     {
    439       fprintf (stderr, "Unexpected 'cnonce_len'.\n"
    440                "Expected: %d\tRecieved: %ld. ",
    441                (int) MHD_STATICSTR_LEN_ (CNONCE_EMU),
    442                (long) dinfo->cnonce_len);
    443       mhdErrorExitDesc ("Wrong 'cnonce_len'");
    444     }
    445     else if (NULL == dinfo->opaque)
    446       mhdErrorExitDesc ("'opaque' is NULL");
    447     else if (dinfo->opaque_len != MHD_STATICSTR_LEN_ (OPAQUE_VALUE))
    448     {
    449       fprintf (stderr, "'opaque_len' does not match.\n"
    450                "Expected: %u\tRecieved: %u. ",
    451                (unsigned) MHD_STATICSTR_LEN_ (OPAQUE_VALUE),
    452                (unsigned) dinfo->opaque_len);
    453       mhdErrorExitDesc ("Wrong 'opaque_len'");
    454     }
    455     else if (0 != memcmp (dinfo->opaque, OPAQUE_VALUE, dinfo->opaque_len))
    456     {
    457       fprintf (stderr, "'opaque' does not match.\n"
    458                "Expected: '%s'\tRecieved: '%.*s'. ",
    459                OPAQUE_VALUE,
    460                (int) dinfo->opaque_len,
    461                dinfo->opaque);
    462       mhdErrorExitDesc ("Wrong 'opaque'");
    463     }
    464     else if (MHD_DIGEST_AUTH_QOP_AUTH != dinfo->qop)
    465     {
    466       fprintf (stderr, "Unexpected 'qop'.\n"
    467                "Expected: %d\tRecieved: %d. ",
    468                (int) MHD_DIGEST_AUTH_QOP_AUTH,
    469                (int) dinfo->qop);
    470       mhdErrorExitDesc ("Wrong 'qop'");
    471     }
    472     else if (NULL == dinfo->realm)
    473       mhdErrorExitDesc ("'realm' is NULL");
    474     else if (dinfo->realm_len != MHD_STATICSTR_LEN_ (REALM))
    475     {
    476       fprintf (stderr, "'realm_len' does not match.\n"
    477                "Expected: %u\tRecieved: %u. ",
    478                (unsigned) MHD_STATICSTR_LEN_ (REALM),
    479                (unsigned) dinfo->realm_len);
    480       mhdErrorExitDesc ("Wrong 'realm_len'");
    481     }
    482     else if (0 != memcmp (dinfo->realm, REALM, dinfo->realm_len))
    483     {
    484       fprintf (stderr, "'realm' does not match.\n"
    485                "Expected: '%s'\tRecieved: '%.*s'. ",
    486                OPAQUE_VALUE,
    487                (int) dinfo->realm_len,
    488                dinfo->realm);
    489       mhdErrorExitDesc ("Wrong 'realm'");
    490     }
    491     MHD_free (creds);
    492     MHD_free (dinfo);
    493 
    494     check_res = MHD_digest_auth_check3 (connection, REALM, USERNAME,
    495                                         PASSWORD_VALUE,
    496                                         50 * TIMEOUTS_VAL,
    497                                         0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
    498                                         MHD_DIGEST_AUTH_MULT_ALGO3_MD5);
    499 
    500     switch (check_res)
    501     {
    502     /* Valid results */
    503     case MHD_DAUTH_NONCE_STALE:
    504       if (verbose)
    505         printf ("Got valid auth check result: MHD_DAUTH_NONCE_STALE.\n");
    506       break;
    507     case MHD_DAUTH_NONCE_WRONG:
    508       if (verbose)
    509         printf ("Got valid auth check result: MHD_DAUTH_NONCE_WRONG.\n");
    510       break;
    511 
    512     /* Invalid results */
    513     case MHD_DAUTH_OK:
    514       mhdErrorExitDesc ("'MHD_digest_auth_check3()' succeed, " \
    515                         "but it should not");
    516       break;
    517     case MHD_DAUTH_ERROR:
    518       externalErrorExitDesc ("General error returned " \
    519                              "by 'MHD_digest_auth_check3()'");
    520       break;
    521     case MHD_DAUTH_WRONG_USERNAME:
    522       mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
    523                         "MHD_DAUTH_WRONG_USERNAME");
    524       break;
    525     case MHD_DAUTH_RESPONSE_WRONG:
    526       mhdErrorExitDesc ("MHD_digest_auth_check3()' returned " \
    527                         "MHD_DAUTH_RESPONSE_WRONG");
    528       break;
    529     case MHD_DAUTH_WRONG_HEADER:
    530     case MHD_DAUTH_WRONG_REALM:
    531     case MHD_DAUTH_WRONG_URI:
    532     case MHD_DAUTH_WRONG_QOP:
    533     case MHD_DAUTH_WRONG_ALGO:
    534     case MHD_DAUTH_TOO_LARGE:
    535     case MHD_DAUTH_NONCE_OTHER_COND:
    536       fprintf (stderr, "'MHD_digest_auth_check3()' returned "
    537                "unexpected result: %d. ",
    538                check_res);
    539       mhdErrorExitDesc ("Wrong returned code");
    540       break;
    541     default:
    542       fprintf (stderr, "'MHD_digest_auth_check3()' returned "
    543                "impossible result code: %d. ",
    544                check_res);
    545       mhdErrorExitDesc ("Impossible returned code");
    546     }
    547 
    548     response =
    549       MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
    550                                               (const void *) DENIED);
    551     if (NULL == response)
    552       mhdErrorExitDesc ("Response creation failed");
    553     ret = MHD_queue_auth_fail_response2 (connection, REALM, OPAQUE_VALUE,
    554                                          response, 0, MHD_DIGEST_ALG_MD5);
    555     if (MHD_YES != ret)
    556       mhdErrorExitDesc ("'MHD_queue_auth_fail_response2()' failed");
    557   }
    558   else
    559   {
    560     char *username;
    561     int check_res;
    562 
    563     username = MHD_digest_auth_get_username (connection);
    564     if (NULL == username)
    565       mhdErrorExitDesc ("'MHD_digest_auth_get_username()' returned NULL");
    566     else if (0 != strcmp (username, USERNAME))
    567     {
    568       fprintf (stderr, "'username' does not match.\n"
    569                "Expected: '%s'\tRecieved: '%s'. ",
    570                USERNAME,
    571                username);
    572       mhdErrorExitDesc ("Wrong 'username'");
    573     }
    574     MHD_free (username);
    575 
    576     check_res = MHD_digest_auth_check (connection, REALM, USERNAME,
    577                                        PASSWORD_VALUE,
    578                                        300);
    579 
    580     if (MHD_INVALID_NONCE != check_res)
    581     {
    582       fprintf (stderr, "'MHD_digest_auth_check()' returned unexpected"
    583                " result: %d. ", check_res);
    584       mhdErrorExitDesc ("Wrong 'MHD_digest_auth_check()' result");
    585     }
    586     response =
    587       MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
    588                                               (const void *) DENIED);
    589     if (NULL == response)
    590       mhdErrorExitDesc ("Response creation failed");
    591 
    592     ret = MHD_queue_auth_fail_response (connection, REALM, OPAQUE_VALUE,
    593                                         response, 0);
    594     if (MHD_YES != ret)
    595       mhdErrorExitDesc ("'MHD_queue_auth_fail_response()' failed");
    596   }
    597 
    598   MHD_destroy_response (response);
    599   return ret;
    600 }
    601 
    602 
    603 static CURL *
    604 setupCURL (void *cbc, uint16_t port)
    605 {
    606   CURL *c;
    607   char url[512];
    608 
    609   if (1)
    610   {
    611     int res;
    612     /* A workaround for some old libcurl versions, which ignore the specified
    613      * port by CURLOPT_PORT when authorisation is used. */
    614     res = snprintf (url, (sizeof(url) / sizeof(url[0])),
    615                     "http://127.0.0.1:%u%s",
    616                     (unsigned int) port, MHD_URI_BASE_PATH);
    617     if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
    618       externalErrorExitDesc ("Cannot form request URL");
    619   }
    620 
    621   c = curl_easy_init ();
    622   if (NULL == c)
    623     libcurlErrorExitDesc ("curl_easy_init() failed");
    624 
    625   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    626       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    627                                      &copyBuffer)) ||
    628       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
    629       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    630                                      ((long) TIMEOUTS_VAL))) ||
    631       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    632                                      CURL_HTTP_VERSION_1_1)) ||
    633       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    634                                      ((long) TIMEOUTS_VAL))) ||
    635       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    636                                      libcurl_errbuf)) ||
    637       /* (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) || */
    638       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) ||
    639       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, curl_headers)) ||
    640 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    641       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) ||
    642 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    643       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
    644 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    645 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    646       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
    647 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    648       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) ||
    649       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url)))
    650     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    651   return c;
    652 }
    653 
    654 
    655 static CURLcode
    656 performQueryExternal (struct MHD_Daemon *d, CURL *c)
    657 {
    658   CURLM *multi;
    659   time_t start;
    660   struct timeval tv;
    661   CURLcode ret;
    662 
    663   ret = CURLE_FAILED_INIT; /* will be replaced with real result */
    664   multi = NULL;
    665   multi = curl_multi_init ();
    666   if (multi == NULL)
    667     libcurlErrorExitDesc ("curl_multi_init() failed");
    668   if (CURLM_OK != curl_multi_add_handle (multi, c))
    669     libcurlErrorExitDesc ("curl_multi_add_handle() failed");
    670 
    671   start = time (NULL);
    672   while (time (NULL) - start <= TIMEOUTS_VAL)
    673   {
    674     fd_set rs;
    675     fd_set ws;
    676     fd_set es;
    677     MHD_socket maxMhdSk;
    678     int maxCurlSk;
    679     int running;
    680 
    681     maxMhdSk = MHD_INVALID_SOCKET;
    682     maxCurlSk = -1;
    683     FD_ZERO (&rs);
    684     FD_ZERO (&ws);
    685     FD_ZERO (&es);
    686     if (NULL != multi)
    687     {
    688       curl_multi_perform (multi, &running);
    689       if (0 == running)
    690       {
    691         struct CURLMsg *msg;
    692         int msgLeft;
    693         int totalMsgs = 0;
    694         do
    695         {
    696           msg = curl_multi_info_read (multi, &msgLeft);
    697           if (NULL == msg)
    698             libcurlErrorExitDesc ("curl_multi_info_read() failed");
    699           totalMsgs++;
    700           if (CURLMSG_DONE == msg->msg)
    701             ret = msg->data.result;
    702         } while (msgLeft > 0);
    703         if (1 != totalMsgs)
    704         {
    705           fprintf (stderr,
    706                    "curl_multi_info_read returned wrong "
    707                    "number of results (%d).\n",
    708                    totalMsgs);
    709           externalErrorExit ();
    710         }
    711         curl_multi_remove_handle (multi, c);
    712         curl_multi_cleanup (multi);
    713         multi = NULL;
    714       }
    715       else
    716       {
    717         if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
    718           libcurlErrorExitDesc ("curl_multi_fdset() failed");
    719       }
    720     }
    721     if (NULL == multi)
    722     { /* libcurl has finished, check whether MHD still needs to perform cleanup */
    723       if (0 != MHD_get_timeout64s (d))
    724         break; /* MHD finished as well */
    725     }
    726     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
    727       mhdErrorExitDesc ("MHD_get_fdset() failed");
    728     tv.tv_sec = 0;
    729     tv.tv_usec = 200000;
    730 #ifdef MHD_POSIX_SOCKETS
    731     if (maxMhdSk > maxCurlSk)
    732       maxCurlSk = maxMhdSk;
    733 #endif /* MHD_POSIX_SOCKETS */
    734     if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
    735     {
    736 #ifdef MHD_POSIX_SOCKETS
    737       if (EINTR != errno)
    738         externalErrorExitDesc ("Unexpected select() error");
    739 #else
    740       if ((WSAEINVAL != WSAGetLastError ()) ||
    741           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    742         externalErrorExitDesc ("Unexpected select() error");
    743       Sleep (200);
    744 #endif
    745     }
    746     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
    747       mhdErrorExitDesc ("MHD_run_from_select() failed");
    748   }
    749 
    750   return ret;
    751 }
    752 
    753 
    754 /**
    755  * Check request result
    756  * @param curl_code the CURL easy return code
    757  * @param pcbc the pointer struct CBC
    758  * @return non-zero if success, zero if failed
    759  */
    760 static unsigned int
    761 check_result (CURLcode curl_code, CURL *c, struct CBC *pcbc)
    762 {
    763   long code;
    764   if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
    765     libcurlErrorExit ();
    766 
    767   if (401 != code)
    768   {
    769     fprintf (stderr, "Request returned wrong code: %ld.\n",
    770              code);
    771     return 0;
    772   }
    773 
    774   if (CURLE_OK != curl_code)
    775   {
    776     fflush (stdout);
    777     if (0 != libcurl_errbuf[0])
    778       fprintf (stderr, "Request failed. "
    779                "libcurl error: '%s'.\n"
    780                "libcurl error description: '%s'.\n",
    781                curl_easy_strerror (curl_code),
    782                libcurl_errbuf);
    783     else
    784       fprintf (stderr, "Request failed. "
    785                "libcurl error: '%s'.\n",
    786                curl_easy_strerror (curl_code));
    787     fflush (stderr);
    788     return 0;
    789   }
    790 
    791   if (pcbc->pos != strlen (DENIED))
    792   {
    793     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    794              (unsigned) pcbc->pos, (int) pcbc->pos, pcbc->buf,
    795              (unsigned) strlen (DENIED));
    796     mhdErrorExitDesc ("Wrong returned data length");
    797   }
    798   if (0 != memcmp (DENIED, pcbc->buf, pcbc->pos))
    799   {
    800     fprintf (stderr, "Got invalid response '%.*s'. ",
    801              (int) pcbc->pos, pcbc->buf);
    802     mhdErrorExitDesc ("Wrong returned data");
    803   }
    804   return 1;
    805 }
    806 
    807 
    808 static unsigned int
    809 testDigestAuthEmu (void)
    810 {
    811   struct MHD_Daemon *d;
    812   uint16_t port;
    813   struct CBC cbc;
    814   char buf[2048];
    815   CURL *c;
    816   int failed = 0;
    817 
    818   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    819     port = 0;
    820   else
    821     port = 4210;
    822 
    823   d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
    824                         port, NULL, NULL,
    825                         &ahc_echo, NULL,
    826                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    827                         MHD_OPTION_END);
    828   if (d == NULL)
    829     return 1;
    830   if (0 == port)
    831   {
    832     const union MHD_DaemonInfo *dinfo;
    833 
    834     dinfo = MHD_get_daemon_info (d,
    835                                  MHD_DAEMON_INFO_BIND_PORT);
    836     if ( (NULL == dinfo) ||
    837          (0 == dinfo->port) )
    838       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
    839     port = (uint16_t) dinfo->port;
    840   }
    841 
    842   /* First request */
    843   cbc.buf = buf;
    844   cbc.size = sizeof (buf);
    845   cbc.pos = 0;
    846   memset (cbc.buf, 0, cbc.size);
    847   c = setupCURL (&cbc, port);
    848   if (check_result (performQueryExternal (d, c), c, &cbc))
    849   {
    850     if (verbose)
    851       printf ("Got expected response.\n");
    852   }
    853   else
    854   {
    855     fprintf (stderr, "Request FAILED.\n");
    856     failed = 1;
    857   }
    858   curl_easy_cleanup (c);
    859 
    860   MHD_stop_daemon (d);
    861   return failed ? 1 : 0;
    862 }
    863 
    864 
    865 int
    866 main (int argc, char *const *argv)
    867 {
    868   unsigned int errorCount = 0;
    869   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
    870 
    871   verbose = ! (has_param (argc, argv, "-q") ||
    872                has_param (argc, argv, "--quiet") ||
    873                has_param (argc, argv, "-s") ||
    874                has_param (argc, argv, "--silent"));
    875   oldapi = has_in_name (argv[0], "_oldapi");
    876   test_global_init ();
    877 
    878   errorCount += testDigestAuthEmu ();
    879   if (errorCount != 0)
    880     fprintf (stderr, "Error (code: %u)\n", errorCount);
    881   test_global_cleanup ();
    882   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    883 }