libmicrohttpd

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

test_digestauth_emu_ext.c (28861B)


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