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